diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f63c353..8ec616ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: @@ -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 diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index 2d3a9539..f56c3587 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -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. diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml index b5e1cfc6..3f142615 100644 --- a/.github/workflows/mutation.yml +++ b/.github/workflows/mutation.yml @@ -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. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a6349ea9..1962c07d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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: diff --git a/CHANGELOG.md b/CHANGELOG.md index 67209d98..35e87036 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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 @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e77176b6..4f392a10 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/README.md b/README.md index abb1ad75..2c57e566 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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. @@ -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 @@ -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) | @@ -712,6 +713,7 @@ recipe. `tfg formats ` 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` | diff --git a/go.mod b/go.mod index c7cc8e89..f6fd4fa4 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -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 diff --git a/internal/damage/appendjunk.go b/internal/damage/appendjunk.go index ed896fa3..2d0c09d9 100644 --- a/internal/damage/appendjunk.go +++ b/internal/damage/appendjunk.go @@ -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 diff --git a/internal/damage/truncate.go b/internal/damage/truncate.go index c0f38d77..02c7c5d7 100644 --- a/internal/damage/truncate.go +++ b/internal/damage/truncate.go @@ -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. diff --git a/internal/damage/zerohead.go b/internal/damage/zerohead.go index 327bc045..44c4a0c1 100644 --- a/internal/damage/zerohead.go +++ b/internal/damage/zerohead.go @@ -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 diff --git a/internal/format/all/all.go b/internal/format/all/all.go index 69c4f76a..415a3bba 100644 --- a/internal/format/all/all.go +++ b/internal/format/all/all.go @@ -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" diff --git a/internal/format/otlp/layout.go b/internal/format/otlp/layout.go new file mode 100644 index 00000000..9658b8c3 --- /dev/null +++ b/internal/format/otlp/layout.go @@ -0,0 +1,263 @@ +package otlp + +import ( + "encoding/hex" + "strconv" +) + +// layout is how a file is laid out: whether it is cut into lines, one batch +// to a line, and whether every value stands on a line of its own. +type layout struct { + name string + // lines is true for the layout of the OTLP file exporter, where a batch + // ends its line. The other two write the whole file as one batch. + lines bool + // indent puts every value on its own line, two spaces a level, the way + // the examples in the opentelemetry-proto repository are written. + indent bool + // end is what follows the last brace of a batch. + end string +} + +var layouts = map[string]layout{ + // One batch on each line: JSON Lines, which the specification of the OTLP + // file exporter names. + RecordPerLine: {name: RecordPerLine, lines: true, end: "\n"}, + // No whitespace anywhere and no newline at the end, the same promise the + // json format makes for the same word. + Minified: {name: Minified}, + // What JSON.stringify(x, null, 2) writes, and what the OTLP examples look + // like. + Indented: {name: Indented, indent: true, end: "\n"}, +} + +// envelope is the names one kind of telemetry gives the three levels a record +// sits under: the resource, the scope and the records themselves. +type envelope struct { + resources, scopes, records string +} + +var envelopes = map[string]envelope{ + Traces: {"resourceSpans", "scopeSpans", "spans"}, + Logs: {"resourceLogs", "scopeLogs", "logRecords"}, +} + +const ( + // recordDepth is how deep a record sits: inside the document, the array + // of resources, a resource, its array of scopes, a scope and its array of + // records. + recordDepth = 6 + + // paddingKey names the attribute that takes the last bytes of a file. Its + // own prefix, so it cannot be mistaken for an attribute the semantic + // conventions define. + paddingKey = "tfg.padding" +) + +// emitter appends JSON tokens with the whitespace its layout asks for. +// +// It appends into one slice rather than writing to a stream, because the +// closing record has to be measured before its padding is written. The same +// shape as the emitter of the geojson format, which nests for the same reason. +type emitter struct { + b []byte + indent bool + depth int + // fresh is true right after a container opens, before its first element, + // which is the one element not preceded by a comma. + fresh bool +} + +// indentation is enough spaces for the deepest token a record has: the value +// of an attribute sits ten levels down, twenty spaces. +const indentation = " " + +func (e *emitter) newline() { + e.b = append(e.b, '\n') + e.b = append(e.b, indentation[:2*e.depth]...) +} + +// next is what comes before any element of a container - a member of an object +// or a value of an array. +func (e *emitter) next() { + if !e.fresh { + e.b = append(e.b, ',') + } + e.fresh = false + if e.indent { + e.newline() + } +} + +func (e *emitter) open(c byte) { + e.b = append(e.b, c) + e.depth++ + e.fresh = true +} + +// close ends a container that holds at least one element. An empty one is +// written by nothing. +func (e *emitter) close(c byte) { + e.depth-- + if e.indent { + e.newline() + } + e.fresh = false + e.b = append(e.b, c) +} + +func (e *emitter) key(k string) { + e.next() + e.b = append(e.b, '"') + e.b = append(e.b, k...) + e.b = append(e.b, '"', ':') + if e.indent { + e.b = append(e.b, ' ') + } +} + +// text is a string value. Every string this format writes comes from its own +// vocabulary, none of which needs escaping. +func (e *emitter) text(v string) { + e.b = append(e.b, '"') + e.b = append(e.b, v...) + e.b = append(e.b, '"') +} + +func (e *emitter) whole(v int64) { e.b = strconv.AppendInt(e.b, v, 10) } + +// quoted is a whole number written as text, which is how the JSON encoding of +// OTLP writes every number of 64 bits, a time among them. +func (e *emitter) quoted(v int64) { e.numbered("", v) } + +// numbered is a string value made of a prefix and a whole number, appended +// straight into the file. +// +// Never through strconv.Itoa, which builds a new string for every number from +// 100 up. Measured on 2026-11-11: a client address and a response code built +// that way cost 37292 objects for a 64 MiB file of spans, against a budget of +// 128, and TestNoGeneratorHoldsTheWholeFileInMemory turned CI red on it. +func (e *emitter) numbered(prefix string, v int64) { + e.b = append(e.b, '"') + e.b = append(e.b, prefix...) + e.whole(v) + e.b = append(e.b, '"') +} + +// nothing is an object with no member, written the same in every layout. +func (e *emitter) nothing() { e.b = append(e.b, '{', '}') } + +// id is a trace id or a span id, two hex digits a byte. Lower case because +// that is what the Collector writes back, though the specification takes +// either. +func (e *emitter) id(id []byte) { + e.b = append(e.b, '"') + e.b = hex.AppendEncode(e.b, id) + e.b = append(e.b, '"') +} + +// attribute opens one attribute through the brace of its value. +func (e *emitter) attribute(key string) { + e.next() + e.open('{') + e.key("key") + e.text(key) + e.key("value") + e.open('{') +} + +// shut closes the value of an attribute and the attribute. +func (e *emitter) shut() { + e.close('}') + e.close('}') +} + +func (e *emitter) textAttribute(key, value string) { + e.attribute(key) + e.key("stringValue") + e.text(value) + e.shut() +} + +// wholeAttribute is an attribute holding a whole number, which the encoding +// writes as text like every other number of 64 bits. +func (e *emitter) wholeAttribute(key string, value int64) { + e.attribute(key) + e.key("intValue") + e.quoted(value) + e.shut() +} + +// numberedAttribute is an attribute holding text that ends in a whole number, +// written without building the text first. +func (e *emitter) numberedAttribute(key, prefix string, value int64) { + e.attribute(key) + e.key("stringValue") + e.numbered(prefix, value) + e.shut() +} + +// padding writes the padding attribute with nothing in it and returns where +// its value begins, so the closing record can be measured first and filled +// afterwards. +func (e *emitter) padding() (at int) { + e.attribute(paddingKey) + e.key("stringValue") + e.b = append(e.b, '"') + at = len(e.b) + e.b = append(e.b, '"') + e.shut() + return at +} + +// opening is what starts a batch of this kind of telemetry, through the +// bracket that opens its records: the resource the records come from and the +// scope that wrote them. +func opening(names envelope, indent bool) []byte { + e := &emitter{indent: indent} + e.open('{') + e.key(names.resources) + e.open('[') + e.next() + e.open('{') + e.key("resource") + e.open('{') + e.key("attributes") + e.open('[') + e.textAttribute("service.name", serviceName) + e.textAttribute("service.version", serviceVersion) + e.close(']') + e.close('}') + e.key(names.scopes) + e.open('[') + e.next() + e.open('{') + e.key("scope") + e.open('{') + e.key("name") + e.text(scopeName) + e.key("version") + e.text(serviceVersion) + e.close('}') + e.key(names.records) + e.open('[') + return e.b +} + +// closing is what ends a batch, from the bracket that closes its records. The +// schema address follows the records it describes at both levels, which is +// where the Collector writes it. +func closing(l layout) []byte { + e := &emitter{indent: l.indent, depth: recordDepth} + e.close(']') + e.key("schemaUrl") + e.text(schemaURL) + e.close('}') + e.close(']') + e.key("schemaUrl") + e.text(schemaURL) + e.close('}') + e.close(']') + e.close('}') + return append(e.b, l.end...) +} diff --git a/internal/format/otlp/logs.go b/internal/format/otlp/logs.go new file mode 100644 index 00000000..8645cf71 --- /dev/null +++ b/internal/format/otlp/logs.go @@ -0,0 +1,102 @@ +package otlp + +import ( + // D11 promises the same bytes from the same seed, so a deliberate, + // reproducible generator is the product rather than a weakness. Nothing + // here ever makes a secret. + // nosemgrep: go.lang.security.audit.crypto.math_random.math-random-used + "math/rand/v2" +) + +// sampled is the one trace flag a log record carries here: the request it was +// written in was recorded. +const sampled = 1 + +// entry is everything one log record says. +type entry struct { + at int64 + level level + message string + function string + line int64 + + trace traceID + span spanID +} + +// drawEntry draws the next log record of the file. n is how many came before +// it, which is what moves the clock. +// +// The order of the draws is part of the format, for the reason drawSpan gives. +// The ids are drawn whether or not the file carries them, so turning the trace +// context off changes nothing else about a record. +func (r *records) drawEntry(rng *rand.Rand, n int64) entry { + lv := r.s.levels[rng.IntN(len(r.s.levels))] + en := entry{ + at: r.s.start + n*r.s.step(), + level: lv, + message: lv.messages[rng.IntN(len(lv.messages))], + function: functions[rng.IntN(len(functions))], + line: firstLine + rng.Int64N(lastLine-firstLine+1), + } + drawID(en.trace[:], rng) + drawID(en.span[:], rng) + return en +} + +// widestEntries are the longest log records the settings can draw, one for +// each severity of the mix with the longest thing it says. None when the file +// holds no log records. +func (r *records) widestEntries() []entry { + if r.s.signal != Logs { + return nil + } + var out []entry + for _, lv := range r.s.levels { + out = append(out, entry{at: r.s.start, level: lv, message: longest(lv.messages), function: longest(functions), line: lastLine}) + } + return out +} + +// writeEntry writes one log record, and its padding attribute when it is the +// closing one. at is where the padding begins, nought without one. +func (r *records) writeEntry(en entry, padded bool) (at int) { + e := &r.e + e.next() + e.open('{') + e.key("timeUnixNano") + e.quoted(en.at) + // The moment a collector saw the record. A record an SDK wrote is seen + // the moment it happens, so the two are the same. + e.key("observedTimeUnixNano") + e.quoted(en.at) + e.key("severityNumber") + e.whole(en.level.number) + e.key("severityText") + e.text(en.level.text) + e.key("body") + e.open('{') + e.key("stringValue") + e.text(en.message) + e.close('}') + e.key("attributes") + e.open('[') + e.textAttribute("code.function.name", en.function) + e.wholeAttribute("code.line.number", en.line) + if padded { + at = e.padding() + } + e.close(']') + if r.s.traceContext { + // After the attributes, which is where logs.proto declares them and + // where the Collector writes them. + e.key("flags") + e.whole(sampled) + e.key("traceId") + e.id(en.trace[:]) + e.key("spanId") + e.id(en.span[:]) + } + e.close('}') + return at +} diff --git a/internal/format/otlp/otlp.go b/internal/format/otlp/otlp.go new file mode 100644 index 00000000..c5d6240b --- /dev/null +++ b/internal/format/otlp/otlp.go @@ -0,0 +1,127 @@ +// Package otlp generates OpenTelemetry data in the JSON encoding of OTLP: a +// file of spans or of log records, laid out as the OTLP file exporter writes +// it or as one request for an OTLP endpoint. +// +// What makes this format worth its place is how quietly its readers disagree. +// Measured on 2026-11-10 (docs/OTLP-2026-11-10.md section 3): the file +// receiver of the OpenTelemetry Collector drops a line longer than 1 MiB +// without a word in its log, its HTTP receiver answers 200 to a file of +// several lines and keeps the first, and the JSON parser of the protobuf +// library reads a correct trace id as 24 bytes of something else. A file that +// is valid and exactly as large as asked is how a tester finds out which of +// those the system under test does. +// +// Every batch is spelled the way the Collector writes one back, key for key +// and value for value, so the reference reader can be asked for more than +// "did you read it" - see section 5 of the same document. Laid out in lines a +// file comes back from it byte for byte. Laid out as one request it comes +// back the same once the whitespace between its tokens is taken out, because +// the Collector writes no whitespace of its own. +package otlp + +import ( + "context" + "fmt" + "io" + + "github.com/donislawdev/TestingFilesGenerator/internal/core" + "github.com/donislawdev/TestingFilesGenerator/internal/format" +) + +const generatorVersion = "1" + +func init() { + format.Register(format.Descriptor{ + ID: "otlp", + Name: "OpenTelemetry Protocol JSON", + Extension: ".jsonl", + // jsonlines.org names this type and says it is not registered yet. The + // request body of OTLP over HTTP is application/json, and a file of + // several lines is not one JSON document, so that type would be wrong + // for the default layout. + MediaType: "application/jsonl", + Fidelity: format.FidelityFull, + Determinism: format.DeterminismByte, + + // The specification asks senders not to write an envelope with nothing + // in it, so the smallest file is one batch holding one record. The + // DEFAULT settings' minimum, the way json and geojson declare theirs. + MinBytes: minimumBytes(defaultSettings()), + + // Whole records fill the file and the padding attribute of the last + // one takes the remainder, which is always shorter than a record. One + // long value would be the wrong channel twice over: a line longer than + // 1 MiB is dropped by the file receiver, and a backend may cut a long + // attribute short. + Padding: format.PaddingChannel{ + Name: "the " + paddingKey + " attribute of the last span or log record", + Where: format.PlacementEnd, + Capacity: 0, + }, + + // The label never reaches the content. An attribute more on every + // record changes what a receiver stores, so the file name and the + // manifest carry it instead. + Label: format.LabelExternalOnly, + Oracle: "otelcol", + Properties: properties(), + GeneratorVersion: generatorVersion, + Generator: generator{}, + }) +} + +type generator struct{} + +type memo struct { + seed uint64 + s settings +} + +func defaultSettings() settings { + s, err := parse(nil) + if err != nil { + panic(fmt.Sprintf("otlp: the default settings do not parse: %v", err)) + } + return s +} + +// minimumBytes is one batch holding the longest closing record the settings +// can write, measured rather than written down. +func minimumBytes(s settings) int64 { return newRecords(s).Shortest() } + +func (generator) Plan(r format.Request) (format.Plan, error) { + s, err := parse(r.Properties) + if err != nil { + return format.Plan{}, err + } + if min := minimumBytes(s); r.Bytes < min { + return format.Plan{}, &format.BelowMinimumError{ + Format: "OTLP", + Requested: r.Bytes, + Minimum: min, + Reason: core.Says("otlp.AFileHoldsWholeRecords", "a file holds whole records, and the longest one these settings can write needs that much"), + Hint: core.Says("format.AskForBOrMore", "Ask for %d B or more.", core.A("Min", min)), + } + } + if err := s.endsInTime(r.Bytes); err != nil { + return format.Plan{}, err + } + return format.Plan{ + Bytes: r.Bytes, + Exact: true, + Determinism: format.DeterminismByte, + Properties: s.recorded(), + Memo: memo{seed: r.Seed, s: s}, + }, nil +} + +func (generator) Write(ctx context.Context, w io.Writer, p format.Plan) error { + m, ok := p.Memo.(memo) + if !ok { + return core.Defect(fmt.Errorf("otlp: the plan was not produced by this generator")) + } + // No prologue of its own: what opens a batch is written with the first + // record of that batch, because in the default layout every line opens + // one. + return core.FillRecords(ctx, w, core.NewRand(m.seed), p.Bytes, newRecords(m.s)) +} diff --git a/internal/format/otlp/records.go b/internal/format/otlp/records.go new file mode 100644 index 00000000..e8234ed9 --- /dev/null +++ b/internal/format/otlp/records.go @@ -0,0 +1,138 @@ +package otlp + +import ( + // D11 promises the same bytes from the same seed, so a deliberate, + // reproducible generator is the product rather than a weakness. Nothing + // here ever makes a secret. + // nosemgrep: go.lang.security.audit.crypto.math_random.math-random-used + "math/rand/v2" + + "github.com/donislawdev/TestingFilesGenerator/internal/core" +) + +// records builds the spans or the log records of a file, one at a time, for +// core.FillRecords. +// +// A record here is more than its own braces. The first of a batch carries what +// opens the batch in front of it, and the last carries what closes it, so the +// filler sees whole pieces and a file never ends inside a batch. +type records struct { + s settings + e emitter + // open starts a batch and shut ends one. Both are the same for every + // batch of a file, so they are built once. + open, shut []byte + tail []byte + st, before state + shortest int64 +} + +// state is what the builder remembers from one record to the next. Kept in one +// value so the record built only to learn that it does not fit can be undone +// by putting the value back. +type state struct { + // count is how many records the file holds so far. + count int64 + // inBatch is how many of them the open batch holds. Nought means the next + // record opens a batch. + inBatch int + trace trace +} + +func newRecords(s settings) *records { + r := &records{ + s: s, e: emitter{indent: s.layout.indent}, + open: opening(envelopes[s.signal], s.layout.indent), shut: closing(s.layout), + } + r.shortest = r.measureShortest() + return r +} + +// Shortest is the smallest closing record this builder can end the file with: +// the longest record the settings can draw, with what opens a batch in front +// of it, because the closing record may be the first of its batch. +func (r *records) Shortest() int64 { return r.shortest } + +// Discard takes back everything the thrown away record changed: its place in +// the batch, its tick of the clock and its place in its trace. The closing +// record then carries all three instead. +func (r *records) Discard() { r.st = r.before } + +func (r *records) Append(dst []byte, rng *rand.Rand) []byte { + r.before = r.st + dst, _ = r.record(dst, rng, false) + if r.s.layout.lines && r.st.inBatch == r.s.batch { + dst = append(dst, r.shut...) + r.st.inBatch = 0 + } + return dst +} + +// AppendExact writes the closing record at exactly n bytes, with what ends +// its batch. The padding attribute takes the remainder, which is shorter than +// a record: the filler stops as soon as another whole one would not fit. +func (r *records) AppendExact(dst []byte, rng *rand.Rand, n int64) []byte { + mark := len(dst) + dst, at := r.record(dst, rng, true) + r.tail = append(r.tail[:0], dst[at:]...) + fill := n - int64(len(dst)-mark) - int64(len(r.shut)) + dst = core.AppendFiller(dst[:at], words, fill, nil) + dst = append(dst, r.tail...) + return append(dst, r.shut...) +} + +// record draws the next record and writes it after whatever has to stand in +// front of it: what opens a batch, or the comma after the record before. +func (r *records) record(dst []byte, rng *rand.Rand, padded bool) (out []byte, at int) { + if r.st.inBatch == 0 { + dst = append(dst, r.open...) + } + r.e.b, r.e.depth, r.e.fresh = dst, recordDepth, r.st.inBatch == 0 + if r.s.signal == Traces { + at = r.writeSpan(r.drawSpan(rng), padded) + } else { + at = r.writeEntry(r.drawEntry(rng, r.st.count), padded) + } + r.st.count++ + r.st.inBatch++ + return r.e.b, at +} + +// measureShortest is the closing record at its longest with nothing in its +// padding, opening a batch and ending it. Measured on the bytes rather than +// worked out, like everything here that has to agree with bytes beside it. +func (r *records) measureShortest() int64 { + widest := 0 + for _, sp := range r.widestSpans() { + widest = max(widest, r.measured(func() { r.writeSpan(sp, true) })) + } + for _, en := range r.widestEntries() { + widest = max(widest, r.measured(func() { r.writeEntry(en, true) })) + } + return int64(len(r.open) + widest + len(r.shut)) +} + +// measured is how many bytes write adds as the first record of a batch. +func (r *records) measured(write func()) int { + r.e.b, r.e.depth, r.e.fresh = r.e.b[:0], recordDepth, true + write() + return len(r.e.b) +} + +// LongestLine is the longest line a file of these settings can hold, without +// its line ending, for the guard that keeps every line under LineCeiling. +// +// A full batch of the longest record, and what the closing record can add to +// it. The filler stops only when another whole record would not fit, so the +// closing one is handed up to the smallest closing record and a record more, +// and the smallest closing record counts what opens a batch even where the +// batch is open already. +func LongestLine(props map[string]string) (int64, error) { + s, err := parse(props) + if err != nil { + return 0, err + } + r := newRecords(s) + record := r.shortest - int64(len(r.open)+len(r.shut)) + 1 + return int64(2*len(r.open)) + int64(s.batch+1)*record + int64(len(r.shut)-len(s.layout.end)), nil +} diff --git a/internal/format/otlp/settings.go b/internal/format/otlp/settings.go new file mode 100644 index 00000000..65c66226 --- /dev/null +++ b/internal/format/otlp/settings.go @@ -0,0 +1,337 @@ +package otlp + +import ( + "math" + "strconv" + "time" + + "github.com/donislawdev/TestingFilesGenerator/internal/core" + "github.com/donislawdev/TestingFilesGenerator/internal/format" +) + +// The names of the settings, for the guards that set them. +const ( + Signal = "signal" + Formatting = "formatting" + Batch = "batch" + Start = "start" + Rate = "rate" + Depth = "depth" + StatusMix = "status_mix" + LevelMix = "level_mix" + TraceContext = "trace_context" +) + +// The kinds of telemetry a file can hold. One kind to a file, which is what +// the specification of the OTLP file exporter asks. +const ( + Traces = "traces" + Logs = "logs" +) + +// The layouts, under the names json and geojson use for theirs. +const ( + Indented = "indented" + Minified = "minified" + RecordPerLine = "record-per-line" +) + +const ( + // defaultBatch is the batch an OpenTelemetry SDK exports by default, + // maxExportBatchSize in the specification of the batching span processor. + defaultBatch = 512 + // maxBatch keeps the longest line a batch can make under lineCeiling, and + // a guard measures that it does. + maxBatch = 1000 + + defaultRate = 1 + maxRate = 1_000_000 + + defaultDepth = 3 + maxDepth = 100 + + // defaultStart is the moment the log format starts at, so two files of + // one run tell the same time. + defaultStart = "2026-08-01T12:00:00Z" + startLayout = "2006-01-02T15:04:05Z" + firstYear = 2002 + lastYear = 2199 + + // lastNanos is the last moment a count of nanoseconds in 64 bits with a + // sign can name, in April 2262. From firstYear to there every time has + // nineteen digits, which is what lets the length of a record be known + // before it is drawn. + lastNanos = math.MaxInt64 + + // leastRecordBytes is a length no record is shorter than: its two times + // alone, with their keys, are longer. It bounds how many records a file of + // a given size can hold. + leastRecordBytes = 64 + + // LineCeiling is the longest line the file receiver of the Collector reads + // with its default settings, without the line ending. One byte more and + // the line is dropped without a word, measured on 2026-11-10. + LineCeiling = 1 << 20 +) + +// settings is one file's worth of choices, read and checked. +type settings struct { + signal string + layout layout + batch int + startText string + start int64 + rate int + depth int + statusMix string + statuses []int + levelMix string + levels []level + traceContext bool +} + +// step is how far the clock moves from one trace or log record to the next. +func (s settings) step() int64 { return int64(time.Second) / int64(s.rate) } + +func parse(props map[string]string) (settings, error) { + values := map[string]string{} + for _, p := range properties() { + v, err := valueOf(props, p) + if err != nil { + return settings{}, err + } + values[p.Name] = v + } + batch, _ := strconv.Atoi(values[Batch]) + rate, _ := strconv.Atoi(values[Rate]) + depth, _ := strconv.Atoi(values[Depth]) + s := settings{ + signal: values[Signal], + layout: layouts[values[Formatting]], + batch: batch, + startText: values[Start], + rate: rate, + depth: depth, + statusMix: values[StatusMix], + statuses: statusSets[values[StatusMix]], + levelMix: values[LevelMix], + levels: levelSets[values[LevelMix]], + traceContext: values[TraceContext] == "true", + } + start, err := readStart(s.startText) + if err != nil { + return settings{}, err + } + s.start = start + return s, misplaced(props, s) +} + +func valueOf(props map[string]string, p format.Property) (string, error) { + v, ok := props[p.Name] + if !ok || v == "" { + return p.Default, nil + } + if bad := p.Allows(v); !bad.IsZero() { + return "", &format.PropertyValueError{Format: "otlp", Key: p.Name, Value: v, Reason: bad, Remedy: p.Instead()} + } + return v, nil +} + +// readStart reads the moment the file starts at, as nanoseconds since 1970. +// +// One spelling only, the one `date -u +%FT%TZ` prints. Go's own parser takes a +// fraction of a second after the seconds even when the layout has none, so the +// moment is written back and compared rather than trusted. +func readStart(raw string) (int64, error) { + at, err := time.Parse(startLayout, raw) + if err != nil || at.Format(startLayout) != raw { + return 0, &format.PropertyValueError{ + Format: "otlp", Key: Start, Value: raw, + Reason: core.Says("otlp.ItTakesAMomentInUTC", "it takes a moment in UTC written as 2026-08-01T12:00:00Z"), + Remedy: core.Says("otlp.WriteTheMomentThatWay", "Write the date, a T, the time to the second and a Z, with no fraction of a second."), + } + } + if at.Year() < firstYear || at.Year() > lastYear { + return 0, &format.PropertyValueError{ + Format: "otlp", Key: Start, Value: raw, + Reason: core.Says("otlp.ItHasToLieInTheYears", "it has to lie in the years %d to %d", core.A("First", firstYear), core.A("Last", lastYear)), + Remedy: core.Says("otlp.ChooseAStartInThoseYears", "Choose a start in those years, such as 2026-08-01T12:00:00Z."), + } + } + return at.UnixNano(), nil +} + +// endsInTime refuses a file whose last record would be dated past the last +// time this format writes. +// +// Reachable only by a file of hundreds of gigabytes at a slow rate, and worth +// a refusal all the same: past that moment a time grows a digit, the record +// that carries it is a byte longer than planned, and the file would come out +// the wrong size somewhere near its end. +func (s settings) endsInTime(bytes int64) error { + ticks := bytes / leastRecordBytes + if room := (lastNanos - s.start - int64(longestSpan)) / s.step(); ticks <= room { + return nil + } + // The advice stands in the reason, the way the log format writes a + // conflict: the command line prints the reason and nothing after it, and + // no range declared for start could say what to do here. + return &format.PropertyValueError{ + Format: "otlp", Key: Start, Value: s.startText, + Reason: core.Says("otlp.AFileThisLargeEndsAfter", + "a file of this size at a rate of %d would date its last records after the year 2262. Choose an earlier start, a higher rate or a smaller file", + core.A("Rate", s.rate)), + } +} + +// misplaced refuses a setting somebody chose that can do nothing in this +// file: one that shapes spans beside logs, one that shapes log records beside +// traces, and a batch beside a layout that has none. +// +// Only a real choice can be out of place. A window sends every setting it +// draws, at its default, so a key that arrived says nothing - the same lesson +// the log format wrote down on 2026-08-31. +func misplaced(props map[string]string, s settings) error { + for _, p := range properties() { + v, chosen := props[p.Name] + if !chosen || v == "" || v == p.Default { + continue + } + if why, other := outOfPlace(p.Name, s); !why.IsZero() { + return conflict(p.Name, other, v, why) + } + } + return nil +} + +// outOfPlace says why a setting does nothing beside the rest of s, and which +// other setting it disagrees with. Nothing when it has work to do. +func outOfPlace(name string, s settings) (why core.Said, other string) { + chosen := core.Choice{Of: Signal, Value: s.signal} + switch name { + case Depth, StatusMix: + if s.signal != Traces { + return core.Says("otlp.ItShapesSpansAndSignal", "it shapes spans, and signal %s writes none", core.A("Signal", chosen)), Signal + } + case LevelMix, TraceContext: + if s.signal != Logs { + return core.Says("otlp.ItShapesLogRecordsAndSignal", "it shapes log records, and signal %s writes none", core.A("Signal", chosen)), Signal + } + case Batch: + if !s.layout.lines { + return core.Says("otlp.OnlyOneLayoutHasBatches", "only %s cuts the file into batches, and %s writes one request", + core.A("Lines", core.Choice{Of: Formatting, Value: RecordPerLine}), + core.A("Layout", core.Choice{Of: Formatting, Value: s.layout.name})), Formatting + } + } + return core.Said{}, "" +} + +// conflict names both settings, because naming one of a pair leaves the reader +// to guess which of the two to change. +func conflict(one, other, val string, why core.Said) error { + return &format.PropertyValueError{ + Format: "otlp", Key: one + " and " + other, Value: val, + Subject: core.Says("format.TwoSettings", "%s and %s", core.A("Of", core.LabelTerm(one)), core.A("By", core.LabelTerm(other))), + Reason: core.Says("otlp.DropOneOfTheTwo", "%s. Drop one of the two, or change the other", core.A("Why", why)), + } +} + +// recorded is what the manifest says about the file: only what is true of +// this signal and this layout, so a test never asserts on a setting that left +// no trace in the bytes. +func (s settings) recorded() map[string]any { + out := map[string]any{ + "encoding": "utf-8", + Signal: s.signal, + Formatting: s.layout.name, + Start: s.startText, + Rate: s.rate, + // Stated even though it is always false here, so a test can assert on + // it without knowing which formats carry a label internally. + format.PropertyLabelEmbedded: false, + } + if s.layout.lines { + out[Batch] = s.batch + } + if s.signal == Traces { + out[Depth] = s.depth + out[StatusMix] = s.statusMix + } else { + out[LevelMix] = s.levelMix + out[TraceContext] = s.traceContext + } + return out +} + +func properties() []format.Property { + return append(fileProperties(), recordProperties()...) +} + +// fileProperties are the settings every file has, whatever it holds. +func fileProperties() []format.Property { + return []format.Property{ + { + Name: Signal, Kind: format.PropertyChoice, + Choices: []string{Logs, Traces}, + Default: Traces, + Detail: "Which kind of telemetry the file holds. traces writes spans and logs writes log records. A file holds one kind, as the OTLP file format asks.", + }, + { + Name: Formatting, Kind: format.PropertyChoice, + Choices: []string{Indented, Minified, RecordPerLine}, + Default: RecordPerLine, + Detail: "How the file is laid out. record-per-line puts one batch on each line, which is what the OTLP file exporter writes and the file receiver of the OpenTelemetry Collector reads. " + + "minified and indented make the whole file one request, the body to post to an OTLP endpoint, and a file of that kind is one JSON document whatever its name ends in. " + + "Posted as a body, a file of several lines loses every line after the first in the Collector, and its file receiver reads nothing from an indented file.", + }, + { + Name: Batch, Kind: format.PropertyInt, + Min: 1, Max: maxBatch, + Default: strconv.Itoa(defaultBatch), + Detail: "How many spans or log records each line holds. 512 is the batch an OpenTelemetry SDK sends by default. Only record-per-line has batches, and beside another layout a batch is refused.", + }, + { + Name: Start, Kind: format.PropertyText, + Shape: "a moment in UTC such as 2026-08-01T12:00:00Z", + Default: defaultStart, + Detail: "The moment the first span or log record is dated. The later ones follow it at the rate. Change it when the system that receives the file refuses or hides data that is too old.", + }, + { + Name: Rate, Kind: format.PropertyInt, + Min: 1, Max: maxRate, Unit: "entries per second", + Default: strconv.Itoa(defaultRate), + Detail: "How many traces or log records begin each second.", + }, + } +} + +// recordProperties are the settings that shape one kind of record. Each says +// which kind, because beside the other it is refused. +func recordProperties() []format.Property { + return []format.Property{ + { + Name: Depth, Kind: format.PropertyInt, + Min: 1, Max: maxDepth, + Default: strconv.Itoa(defaultDepth), + Detail: "How many spans each trace has, each one the child of the one before it. 1 leaves every span without a parent. It shapes spans, and beside signal logs it is refused.", + }, + { + Name: StatusMix, Kind: format.PropertyChoice, + Choices: []string{"client-errors", "realistic", "server-errors", "success"}, + Default: "realistic", + Detail: "Which response codes the requests end with. A code of 500 or above marks its span as an error, as the HTTP conventions of OpenTelemetry ask, and every other code leaves the status unset. It shapes spans, and beside signal logs it is refused.", + }, + { + Name: LevelMix, Kind: format.PropertyChoice, + Choices: []string{"debug", "errors", "quiet", "realistic"}, + Default: "realistic", + Detail: "Which severities the log records carry. Realistic is mostly INFO with some of the others. It shapes log records, and beside signal traces it is refused.", + }, + { + Name: TraceContext, Kind: format.PropertyBool, + Default: "true", + Detail: "Whether every log record names the trace and the span it was written in. The file holds no spans, so those ids match nothing in it. It shapes log records, and beside signal traces it is refused.", + }, + } +} diff --git a/internal/format/otlp/spans.go b/internal/format/otlp/spans.go new file mode 100644 index 00000000..733b9f3c --- /dev/null +++ b/internal/format/otlp/spans.go @@ -0,0 +1,226 @@ +package otlp + +import ( + // D11 promises the same bytes from the same seed, so a deliberate, + // reproducible generator is the product rather than a weakness. Nothing + // here ever makes a secret. + // nosemgrep: go.lang.security.audit.crypto.math_random.math-random-used + "math/rand/v2" + + "github.com/donislawdev/TestingFilesGenerator/internal/core" +) + +// The kinds of span a file holds, as trace.proto numbers them. The encoding +// writes the number, never the name. +const ( + kindInternal = 1 + kindServer = 2 + + // statusError is the code of a span that failed. Left out, the status is + // unset, which is what every other span carries. + statusError = 2 +) + +// span is everything one span says, drawn or chosen in one place so the +// smallest file is measured with the same code that writes every other one. +type span struct { + trace traceID + id, parent spanID + // level is how deep the span sits in its trace. Nought is the request + // itself, a span of the server kind, and every other one is work done + // inside it. + level int + start, end int64 + + request request + status int + agent string + client int + + function string +} + +// trace is where the next span belongs: the trace it is part of, the span it +// is the child of and how long the request at the top of it takes. +type trace struct { + // begun is how many traces the file has started, which is what moves the + // clock. + begun int64 + level int + id traceID + parent spanID + start int64 + length int64 +} + +// The two ids of the OTLP data model, sixteen bytes and eight. +type ( + traceID [16]byte + spanID [8]byte +) + +// drawID fills an id from the generator, through the primitive every format +// draws its random bytes with, and keeps it from being all zeros - which the +// specification calls invalid and the Collector reads as no id at all. +func drawID(id []byte, rng *rand.Rand) { + core.FillRandomBE(id, rng) + for _, b := range id { + if b != 0 { + return + } + } + id[len(id)-1] = 1 +} + +// drawSpan draws the next span of the file and moves the trace on. +// +// The order of the draws is part of the format: the same seed has to draw the +// same file for as long as the major version stands (D11), so a draw is never +// moved, only added after the last one of its span. +func (r *records) drawSpan(rng *rand.Rand) span { + t := &r.st.trace + if t.level == 0 { + drawID(t.id[:], rng) + t.start = r.s.start + t.begun*r.s.step() + t.length = int64(shortestSpan) + rng.Int64N(int64(longestSpan-shortestSpan)) + t.begun++ + } + // Every span starts later and ends earlier than its parent by the same + // step, so a child always lies inside the span it belongs to. The request + // is long enough for the deepest trace the settings allow. + inset := int64(t.level) * t.length / int64(2*r.s.depth) + sp := span{trace: t.id, parent: t.parent, level: t.level, start: t.start + inset, end: t.start + t.length - inset} + drawID(sp.id[:], rng) + if sp.level == 0 { + sp.request = requests[rng.IntN(len(requests))] + sp.status = r.s.statuses[rng.IntN(len(r.s.statuses))] + sp.agent = agents[rng.IntN(len(agents))] + sp.client = 1 + rng.IntN(clientHosts) + } else { + sp.function = functions[rng.IntN(len(functions))] + } + t.parent = sp.id + t.level = (t.level + 1) % r.s.depth + return sp +} + +// widestSpans are the longest spans the settings can draw, one of each shape: +// the request and the work inside it. The longer of the two is what the +// smallest file leaves room for. None when the file holds no spans. +// +// Each is dated at the start of the file, because a time is nineteen digits +// from there on and a span dated nought would be measured eighteen short. +func (r *records) widestSpans() []span { + if r.s.signal != Traces { + return nil + } + widest := span{ + request: widestRequest(), status: widestStatus(r.s.statuses), agent: longest(agents), client: clientHosts, + start: r.s.start, end: r.s.start, + } + if r.s.depth == 1 { + return []span{widest} + } + return []span{widest, {level: 1, function: longest(functions), start: r.s.start, end: r.s.start}} +} + +// widestRequest is the request that makes the longest span. Its method and +// its route are each written twice, in the name and in an attribute. +func widestRequest() request { + var widest request + weight := func(q request) int { return 2*len(q.method) + 2*len(q.route) + len(q.path) } + for _, q := range requests { + if weight(q) > weight(widest) { + widest = q + } + } + return widest +} + +// widestStatus is a response code that makes the longest span: one a span +// records as an error, when the mix holds any, because that span carries an +// attribute and a status code more. +func widestStatus(statuses []int) int { + widest := statuses[0] + for _, s := range statuses { + widest = max(widest, s) + } + return widest +} + +// writeSpan writes one span, and its padding attribute when it is the closing +// one. at is where the padding begins, nought without one. +func (r *records) writeSpan(sp span, padded bool) (at int) { + e := &r.e + e.next() + e.open('{') + e.key("traceId") + e.id(sp.trace[:]) + e.key("spanId") + e.id(sp.id[:]) + if sp.level > 0 { + e.key("parentSpanId") + e.id(sp.parent[:]) + } + e.key("name") + if sp.level == 0 { + e.text(sp.request.method + " " + sp.request.route) + e.key("kind") + e.whole(kindServer) + } else { + e.text(sp.function) + e.key("kind") + e.whole(kindInternal) + } + e.key("startTimeUnixNano") + e.quoted(sp.start) + e.key("endTimeUnixNano") + e.quoted(sp.end) + e.key("attributes") + e.open('[') + if sp.level == 0 { + r.requestAttributes(sp) + } else { + e.textAttribute("code.function.name", sp.function) + } + if padded { + at = e.padding() + } + e.close(']') + e.key("status") + if sp.failed() { + e.open('{') + e.key("code") + e.whole(statusError) + e.close('}') + } else { + // Always there, also with nothing in it: that is how the Collector + // writes a span whose status is unset. + e.nothing() + } + e.close('}') + return at +} + +// failed says whether a span is recorded as an error: a request the server +// answered with a code of 500 or above. +func (sp span) failed() bool { return sp.level == 0 && sp.status >= serverError } + +// requestAttributes are what the HTTP conventions ask a server span to carry, +// under the names version 1.45.0 gives them. +func (r *records) requestAttributes(sp span) { + e := &r.e + e.textAttribute("http.request.method", sp.request.method) + e.textAttribute("http.route", sp.request.route) + e.textAttribute("url.path", sp.request.path) + e.textAttribute("url.scheme", "https") + e.wholeAttribute("http.response.status_code", int64(sp.status)) + e.textAttribute("server.address", serverAddress) + e.numberedAttribute("client.address", clientNetwork, int64(sp.client)) + e.textAttribute("user_agent.original", sp.agent) + if sp.failed() { + // The conventions ask for the class of the error where a request + // ended with one, and for a response code that class is the code. + e.numberedAttribute("error.type", "", int64(sp.status)) + } +} diff --git a/internal/format/otlp/vocabulary.go b/internal/format/otlp/vocabulary.go new file mode 100644 index 00000000..712481a2 --- /dev/null +++ b/internal/format/otlp/vocabulary.go @@ -0,0 +1,140 @@ +package otlp + +import "time" + +// What every file says about where its records come from. One service and one +// scope: a batch a single process exported, which is what an OTLP file from an +// SDK is. +const ( + serviceName = "shop" + serviceVersion = "1.4.2" + scopeName = "shop.server" + serverAddress = "shop.example.com" + + // schemaURL is the version of the semantic conventions the attribute + // names below were read from on 2026-11-10. Every one of them is marked + // stable there. + schemaURL = "https://opentelemetry.io/schemas/1.45.0" + + // clientNetwork is one of the three networks RFC 5737 sets aside for + // documentation, so no address in a file belongs to anybody. + clientNetwork = "203.0.113." + clientHosts = 254 +) + +const ( + // shortestSpan and longestSpan are how long a request takes. The longest + // is counted in when a file is checked to end in time. + shortestSpan = 5 * time.Millisecond + longestSpan = 500 * time.Millisecond + + // The lines a log record says it was written at, three digits wide at + // most, so the widest is what the smallest file leaves room for. + firstLine = 10 + lastLine = 999 +) + +// request is one thing a client asks the service for: the method, the route +// that matched and the path that was sent. +// +// A route and a path rather than a path alone, because the HTTP conventions +// name a span after the route and forbid naming it after the path - a path +// carries an id, and a span name is meant to be the same for every order. +type request struct { + method, route, path string +} + +var requests = []request{ + {"GET", "/", "/"}, + {"GET", "/health", "/health"}, + {"GET", "/search", "/search"}, + {"POST", "/login", "/login"}, + {"GET", "/static/{file}", "/static/app.css"}, + {"GET", "/api/v1/orders", "/api/v1/orders"}, + {"POST", "/api/v1/orders", "/api/v1/orders"}, + {"GET", "/api/v1/orders/{id}", "/api/v1/orders/4711"}, + {"PUT", "/api/v1/orders/{id}", "/api/v1/orders/4711"}, + {"DELETE", "/api/v1/orders/{id}", "/api/v1/orders/1024"}, + {"GET", "/api/v1/customers/{id}", "/api/v1/customers/308"}, + {"GET", "/api/v1/invoices", "/api/v1/invoices"}, + {"GET", "/api/v1/invoices/{id}/pdf", "/api/v1/invoices/9001/pdf"}, +} + +// statusSets are the mixes of response codes, under the names the log format +// gives the same four. Repeats are the weighting. +var statusSets = map[string][]int{ + "realistic": {200, 200, 200, 201, 204, 301, 302, 304, 400, 401, 403, 404, 409, 429, 500, 502, 503}, + "success": {200, 200, 201, 204, 206}, + "client-errors": {400, 401, 403, 404, 409, 410, 422, 429}, + "server-errors": {500, 500, 502, 503, 504}, +} + +// serverError is the first response code the HTTP conventions tell a server +// span to record as an error. A code of the 4xx range leaves the status unset +// on a server span. +const serverError = 500 + +var agents = []string{ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/128.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/605.1.15", + "Mozilla/5.0 (X11; Linux x86_64) Chrome/126.0.0.0 Safari/537.36", + "curl/8.7.1", + "python-requests/2.32.3", + "Go-http-client/2.0", +} + +// functions are what the service does inside a request, and where a log +// record says it was written. +var functions = []string{ + "shop.auth.authorize", "shop.cache.lookup", "shop.orders.load", "shop.orders.validate", + "shop.stock.reserve", "shop.payments.charge", "shop.invoices.render", "shop.mail.send", +} + +// level is one severity: the word a log record carries and the number the +// OTLP data model gives it. +type level struct { + text string + number int64 + // messages are what a record of this severity says, so an ERROR never + // reports that an order was accepted. + messages []string +} + +// The four severities, with the numbers logs.proto gives the first of each +// range. +var ( + levelDebug = level{"DEBUG", 5, []string{"cache miss", "loading order", "query finished", "stock checked"}} + levelInfo = level{"INFO", 9, []string{"order accepted", "invoice sent", "customer signed in", "report generated"}} + levelWarn = level{"WARN", 13, []string{"slow query", "retrying payment", "stock running low"}} + levelError = level{"ERROR", 17, []string{"payment refused", "invoice could not be rendered", "upstream timed out"}} +) + +// levelSets are the mixes of severities, under the names and in the shares the +// log format gives the same four. Repeats are the weighting. +var levelSets = map[string][]level{ + "realistic": {levelInfo, levelInfo, levelInfo, levelWarn, levelError, levelDebug}, + "quiet": {levelInfo}, + "errors": {levelError, levelError, levelError, levelWarn, levelWarn, levelInfo}, + "debug": {levelDebug, levelDebug, levelDebug, levelInfo, levelInfo, levelWarn}, +} + +// words fill the padding attribute of the last record. Words and single +// spaces only - a JSON string would otherwise need escaping, and an escape +// costs a byte the size arithmetic did not budget for. +var words = []string{ + "account", "address", "amount", "balance", "batch", "buyer", "carrier", "contract", + "credit", "customer", "delivery", "deposit", "discount", "invoice", "ledger", "order", + "parcel", "payment", "receipt", "refund", "region", "reseller", "shipment", "supplier", +} + +// longest is the longest of a list of texts, because the smallest file has to +// leave room for the widest draw rather than for the lucky one. +func longest(texts []string) string { + out := "" + for _, t := range texts { + if len(t) > len(out) { + out = t + } + } + return out +} diff --git a/internal/guard/choiceseed_test.go b/internal/guard/choiceseed_test.go index 8339fed2..b8a6d315 100644 --- a/internal/guard/choiceseed_test.go +++ b/internal/guard/choiceseed_test.go @@ -110,6 +110,11 @@ var choiceCompanions = map[string]map[string]string{ "log.level_mix=debug": {"entry_format": "plain"}, "log.level_mix=errors": {"entry_format": "plain"}, "log.level_mix=quiet": {"entry_format": "plain"}, + // A setting that shapes log records needs a file of them. + "otlp.level_mix=debug": {"signal": "logs"}, + "otlp.level_mix=errors": {"signal": "logs"}, + "otlp.level_mix=quiet": {"signal": "logs"}, + "otlp.trace_context=false": {"signal": "logs"}, // XML in UTF-16 opens with a byte order mark. "xml.encoding=utf-16be": {"bom": "true"}, "xml.encoding=utf-16le": {"bom": "true"}, diff --git a/internal/guard/codeshape_test.go b/internal/guard/codeshape_test.go index 36d4b1db..ba7dc0d1 100644 --- a/internal/guard/codeshape_test.go +++ b/internal/guard/codeshape_test.go @@ -68,7 +68,11 @@ const ( // Held at 395 on 2026-11-03: the list of batches moved out of // gui/window/recipe.go into recipelist.go when one import took it past // the ceiling. The longest file is oracle/oracle.go now. - longestFile = 395 + // Lowered from 395 on 2026-11-11: the caller of the structural checker + // moved out of oracle/oracle.go into strict.go when the format it + // learned took the file past the ceiling. The longest file is + // engine/engine.go now. + longestFile = 392 // Depth answers a different question than length, and it is the better // question of the two. A hundred line function that is flat reads top to diff --git a/internal/guard/contextroots_test.go b/internal/guard/contextroots_test.go index 4084266a..455d64c4 100644 --- a/internal/guard/contextroots_test.go +++ b/internal/guard/contextroots_test.go @@ -30,7 +30,7 @@ var contextRoots = map[string]string{ "internal/gui/window/work.go begin": "a preview, a run or a tool started in the window begins here, and Cancel or closing the window is what stops it", "internal/oracle/oracle.go Checker.Check": "the oracle runs somebody else's reader under a time limit of its own - a measuring instrument for the guards, never on the path of a run", "internal/oracle/oracle.go PictureChanges": "the same instrument, a reference reader under a time limit", - "internal/oracle/oracle.go Strict": "the same instrument, a reference reader under a time limit", + "internal/oracle/strict.go Strict": "the same instrument, a reference reader under a time limit", "internal/engine/engine.go Plan": "the version without a context, for callers that have none - it is PlanContext, so it cannot disagree with it", "internal/manifest/manifest.go Load": "reads a document of at most sixteen megabytes that is in memory already, so there is nothing long to stop - verify and cleanup read through ReadClaims, which takes the caller's", "internal/format/eml/eml.go generator.Plan": "the Generator interface's Plan for callers without a context - the engine asks PlanContext", diff --git a/internal/guard/damagecommand_test.go b/internal/guard/damagecommand_test.go index 755cdb5c..219a2c3d 100644 --- a/internal/guard/damagecommand_test.go +++ b/internal/guard/damagecommand_test.go @@ -290,9 +290,13 @@ func TestTheMachineReadableDamageListCarriesTheWholeDeclaration(t *testing.T) { // description of one damage could drop the reason, and every test stayed // green. Asked for by the review of 2026-11-10. // -// Rebuilt from the declarations, like the guard on settings. A damage with no -// case to declare is the control: it has to stay silent on the subject, or a -// build printing the same lines under every damage would pass. +// Rebuilt from the declarations, like the guard on settings. The control is +// the reasons of the other damages: a description may carry its own and none +// of theirs, or a build printing the same lines under every damage would pass. +// Until 2026-11-11 the control was zero-head, which declared no case at all. +// It declares two since then - a log and an OTLP file of lines, where a reader +// of lines loses one line and keeps the rest - so no damage is silent any +// more, and a damage with nothing to declare is still held to saying nothing. func TestTheDamageCommandSaysInWordsWhereTheOutcomeDependsOnTheReader(t *testing.T) { code, listing, errOut := run(t, "damage") if code != cli.ExitOK { @@ -305,21 +309,30 @@ func TestTheDamageCommandSaysInWordsWhereTheOutcomeDependsOnTheReader(t *testing } } - dependent, certain := 0, 0 + dependent, foreign := 0, 0 for _, d := range damage.All() { code, described, errOut := run(t, "damage", d.ID) if code != cli.ExitOK { t.Fatalf("describing %q ended with %d: %s", d.ID, code, errOut) } under := strings.Join(listedUnder(listing, d.ID), "\n") + own := map[string]bool{} + for _, c := range d.Policy { + own[c.Why] = true + } + for _, why := range reasons { + if own[why] { + continue + } + foreign++ + if strings.Contains(described, why) { + t.Errorf("%s does not declare the case %q, and its description carries it:\n%s", d.ID, why, described) + } + } if len(d.Policy) == 0 { - certain++ if strings.TrimSpace(under) != d.Detail { t.Errorf("%s declares no format where readers differ, and the list prints more than its sentence under it:\n%s", d.ID, under) } - if slices.ContainsFunc(reasons, func(why string) bool { return strings.Contains(described, why) }) { - t.Errorf("%s declares no format where readers differ, and its description carries another damage's reason:\n%s", d.ID, described) - } continue } dependent++ @@ -337,9 +350,9 @@ func TestTheDamageCommandSaysInWordsWhereTheOutcomeDependsOnTheReader(t *testing } } } - if dependent == 0 || certain == 0 { - t.Fatalf("%d damage(s) leave an outcome to the reader and %d leave none - both kinds are needed, or half of this proved nothing", - dependent, certain) + if dependent == 0 || foreign == 0 { + t.Fatalf("%d damage(s) leave an outcome to the reader and %d reason(s) of another damage were looked for - both are needed, or half of this proved nothing", + dependent, foreign) } } diff --git a/internal/guard/damagewitness_test.go b/internal/guard/damagewitness_test.go index 874294b9..d431077d 100644 --- a/internal/guard/damagewitness_test.go +++ b/internal/guard/damagewitness_test.go @@ -90,6 +90,13 @@ var policyRefusals = map[[2]string][]string{ {damage.AppendJunk, "txt"}: {"that are not printable ASCII", "does not decode as utf-16"}, {damage.AppendJunk, "md"}: {"that are not printable ASCII", "does not decode as utf-16"}, // Bytes the format allows: nothing points at them. + // A reader of lines meets the damage on one line and carries on with the + // next, measured 2026-11-11 (docs/OTLP-2026-11-10.md section 12.3). The + // checker names the line the damage is on. + {damage.ZeroHead, "log"}: {"line 1 is not a whole entry in any shape this tool writes", "the file does not end with a newline"}, + {damage.ZeroHead, "otlp"}: {"line 1 is not JSON", "the file does not end with a newline"}, + {damage.Truncate, "otlp"}: {"the file does not end with a newline"}, + {damage.AppendJunk, "otlp"}: {"not valid UTF-8"}, {damage.AppendJunk, "ico"}: {}, {damage.AppendJunk, "tiff"}: {}, } diff --git a/internal/guard/formatlist_test.go b/internal/guard/formatlist_test.go index da9070ce..998abc94 100644 --- a/internal/guard/formatlist_test.go +++ b/internal/guard/formatlist_test.go @@ -211,7 +211,8 @@ func TestTypingIntoTheFormatListNarrowsItAndLandsOnWhatStartsWithIt(t *testing.T // so Down from jxl has a heading in its way - the cost the deferral of // 2026-08-25 wrote down, before there were any headings to step over. geojson // joined the second heading on 2026-10-07, and the walk goes through it both -// ways rather than around it. +// ways rather than around it. otlp joined on 2026-11-11 by its name, which +// ends in JSON, and is where the walk now turns round. func TestTheArrowsInTheFormatListStepOverTheHeadings(t *testing.T) { _, _, list, filter := openFormatList(t) typeInto(filter, "j") @@ -222,7 +223,9 @@ func TestTheArrowsInTheFormatListStepOverTheHeadings(t *testing.T) { {fyne.KeyDown, "jxl"}, {fyne.KeyDown, "geojson"}, {fyne.KeyDown, "json"}, - {fyne.KeyDown, "json"}, + {fyne.KeyDown, "otlp"}, + {fyne.KeyDown, "otlp"}, + {fyne.KeyUp, "json"}, {fyne.KeyUp, "geojson"}, {fyne.KeyUp, "jxl"}, {fyne.KeyUp, "jpg"}, diff --git a/internal/guard/generatorbytes_test.go b/internal/guard/generatorbytes_test.go index ce1734ce..a5f6190c 100644 --- a/internal/guard/generatorbytes_test.go +++ b/internal/guard/generatorbytes_test.go @@ -395,7 +395,7 @@ func goldenCases() map[string]engine.Target { // have to agree to the byte. "targz_past_the_comment_limit": {ID: "g", Format: "targz", Sizes: engine.Uniform(1, 262144), Label: true}, } - for _, more := range []map[string]engine.Target{mapGoldenCases(), mailGoldenCases(), pageGoldenCases(), rtfGoldenCases(), cutLabelGoldenCases()} { + for _, more := range []map[string]engine.Target{mapGoldenCases(), mailGoldenCases(), pageGoldenCases(), rtfGoldenCases(), cutLabelGoldenCases(), telemetryGoldenCases()} { for name, target := range more { cases[name] = target } @@ -440,6 +440,37 @@ func rtfGoldenCases() map[string]engine.Target { } } +// telemetryGoldenCases are the golden cases of the otlp format, a table of +// their own for the reason mailGoldenCases gives. +func telemetryGoldenCases() map[string]engine.Target { + one := func(props map[string]string) engine.Target { + return engine.Target{ID: "g", Format: "otlp", Sizes: engine.Uniform(1, 8192), Label: true, Properties: props} + } + return map[string]engine.Target{ + // One case for each path the bytes take rather than one for each + // setting. Spans in the three layouts, where what opens and ends a + // batch is written by different code each time. Batches of three, so + // a line ends and the next one opens inside the file. Traces of one + // span, which never write a parent, and traces seven deep on a faster + // clock from another year with every request failing, which write the + // error attribute and the status code. The label never reaches the + // content, so it is pinned in one position. + "otlp_8kib": one(nil), + "otlp_8kib_minified": one(map[string]string{"formatting": "minified"}), + "otlp_8kib_indented": one(map[string]string{"formatting": "indented"}), + "otlp_8kib_batches": one(map[string]string{"batch": "3"}), + "otlp_8kib_single_spans": one(map[string]string{"depth": "1"}), + "otlp_8kib_deep_failures": one(map[string]string{"depth": "7", "status_mix": "server-errors", "rate": "1000", "start": "2030-01-01T00:00:00Z"}), + + // Log records the same way: the default, batches of two on the + // fastest clock, and indented with no trace named and a mix that draws + // the longest severity number. + "otlp_8kib_logs": one(map[string]string{"signal": "logs"}), + "otlp_8kib_logs_batches": one(map[string]string{"signal": "logs", "batch": "2", "rate": "1000000"}), + "otlp_8kib_logs_indented_no_trace": one(map[string]string{"signal": "logs", "formatting": "indented", "trace_context": "false", "level_mix": "errors"}), + } +} + // mapGoldenCases are the golden cases of the geojson format, a table of their // own for the reason mailGoldenCases gives. func mapGoldenCases() map[string]engine.Target { diff --git a/internal/guard/layers_test.go b/internal/guard/layers_test.go index 91bb59d2..3263e934 100644 --- a/internal/guard/layers_test.go +++ b/internal/guard/layers_test.go @@ -50,6 +50,7 @@ var layer = map[string]int{ "internal/format/csvfile": 1, "internal/format/jsonfile": 1, "internal/format/geojson": 1, + "internal/format/otlp": 1, "internal/format/eml": 1, "internal/format/rtf": 1, "internal/format/yamlfile": 1, @@ -136,6 +137,7 @@ var sameLayerAllowed = map[string][]string{ "internal/format/csvfile", "internal/format/jsonfile", "internal/format/geojson", + "internal/format/otlp", "internal/format/yamlfile", "internal/format/tomlfile", "internal/format/xmlfile", @@ -173,6 +175,7 @@ var sameLayerAllowed = map[string][]string{ "internal/format/csvfile": {"internal/format"}, "internal/format/jsonfile": {"internal/format"}, "internal/format/geojson": {"internal/format"}, + "internal/format/otlp": {"internal/format"}, "internal/format/yamlfile": {"internal/format"}, "internal/format/tomlfile": {"internal/format", "internal/format/textenc"}, "internal/format/xmlfile": {"internal/format", "internal/format/textenc"}, diff --git a/internal/guard/mutation/entries.py b/internal/guard/mutation/entries.py index 5112dacd..0d4d4319 100644 --- a/internal/guard/mutation/entries.py +++ b/internal/guard/mutation/entries.py @@ -30,7 +30,7 @@ ( "the contributing guide names a Go version this module does not admit", "CONTRIBUTING.md", - "Needs Go 1.27.3 or newer", + "Needs Go 1.27.4 or newer", "Needs Go 1.21 or newer", "TestTheBuildFloorIsThePinnedToolchainAndTheDocumentsSaySo", ), @@ -296,14 +296,14 @@ ( "the README goes back to naming a floor the module no longer admits", "README.md", - "**From source.** Needs Go 1.27.3 or newer, and nothing else:", + "**From source.** Needs Go 1.27.4 or newer, and nothing else:", "**From source.** Needs Go 1.26.5 or newer, and nothing else:", "TestTheBuildFloorIsThePinnedToolchainAndTheDocumentsSaySo", ), ( "CI builds with a compiler older than the floor go.mod admits", ".github/workflows/ci.yml", - 'GO_VERSION: "1.27.3"', + 'GO_VERSION: "1.27.4"', 'GO_VERSION: "1.26.8"', "TestTheBuildFloorIsThePinnedToolchainAndTheDocumentsSaySo", ), @@ -509,7 +509,7 @@ # point: both paths resolve here, so breaking one leaves the other # answering and the guard stays green while proving nothing. "the structural checker looks for an interpreter nobody installed", - "internal/oracle/oracle.go", + "internal/oracle/strict.go", 'python, ok := inPath("python")()', 'python, ok := inPath("python-not-here")()', "TestTheStructuralCheckerIsAvailableWhereItWasBuilt", @@ -520,7 +520,7 @@ # runs to the top of the disk and finds nothing - which is what the # container did for months while every structural check reported a skip. "the walk for the structural checker looks for a name that is not there", - "internal/oracle/oracle.go", + "internal/oracle/strict.go", 'filepath.Join(dir, "internal", "oracle", "strict.py")', 'filepath.Join(dir, "internal", "oracle", "strict.pyc")', "TestTheStructuralCheckerIsFoundByABinaryBuiltSomewhereElse", @@ -530,7 +530,7 @@ # it starts in, so it never climbs to where the script actually is. This # is the state the code was in before the walk existed at all. "the walk for the structural checker stops where it starts", - "internal/oracle/oracle.go", + "internal/oracle/strict.go", "if up == dir {", "if up == dir || true {", "TestTheStructuralCheckerIsFoundByABinaryBuiltSomewhereElse", @@ -3078,7 +3078,7 @@ # simply skips that layer, in silence. That is why the list is declared # and compared rather than trusted. "a format loses its structural check without anything saying so", - "internal/oracle/oracle.go", + "internal/oracle/strict.go", 'case "png", "wav", "pdf", "zip", "targz", "log", "csv", "json", "xml", "svg", "html",', 'case "png", "wav", "pdf", "zip", "log", "csv", "json", "xml", "svg", "html",', "TestTheStructuralCheckerCoversEveryFormatItShould", @@ -17568,4 +17568,106 @@ "\t\tSendAs: \"\",", "TestWhatIsTypedOnTheRecipeScreenIsWhatGetsWritten", ), + ( + # The record built only to learn it does not fit keeps its place in + # the batch, its tick and its place in its trace. The size stays exact + # and every line parses. The checker finds a trace opened a tick late. + "an otlp record thrown away is not undone, so the clock and the traces skip", + "internal/format/otlp/records.go", + "func (r *records) Discard() { r.st = r.before }", + "func (r *records) Discard() {}", + "TestAnOTLPFileIsWhatItsSettingsSay", + ), + ( + # Every child takes the whole of its parent's time. The same number of + # digits, so the size and the Collector's reading do not move. + "an otlp child span is as long as its parent rather than inside it", + "internal/format/otlp/spans.go", + "\tinset := int64(t.level) * t.length / int64(2*r.s.depth)", + "\tinset := int64(0) * t.length / int64(2*r.s.depth)", + "TestAnOTLPFileIsWhatItsSettingsSay", + ), + ( + # A response of exactly 500 stops being an error. The smallest file is + # measured with the largest code of the mix, so no size moves. + "an otlp span that ends with 500 is not recorded as an error", + "internal/format/otlp/spans.go", + "func (sp span) failed() bool { return sp.level == 0 && sp.status >= serverError }", + "func (sp span) failed() bool { return sp.level == 0 && sp.status > serverError }", + "TestAnOTLPFileIsWhatItsSettingsSay", + ), + ( + # Every log record at one instant, which is what the log format wrote + # until it learned to advance. + "otlp log records all carry the moment the file starts at", + "internal/format/otlp/logs.go", + "\t\tat: r.s.start + n*r.s.step(),", + "\t\tat: r.s.start + 0*n*r.s.step(),", + "TestAnOTLPFileIsWhatItsSettingsSay", + ), + ( + # The padding attribute on every log record instead of the last. The + # closing record still takes the remainder, so the size is exact. + "every otlp log record carries the padding attribute", + "internal/format/otlp/logs.go", + "\tif padded {", + "\tif padded || true {", + "TestAnOTLPFileIsWhatItsSettingsSay", + ), + ( + # The bound on a line worked out as if a record were one byte long. + "the longest otlp line is worked out far too short", + "internal/format/otlp/records.go", + "\trecord := r.shortest - int64(len(r.open)+len(r.shut)) + 1", + "\trecord := r.shortest - r.shortest + 1", + "TestNoOTLPLineIsLongerThanTheFileReceiverReads", + ), + ( + "otlp takes a setting that shapes spans beside signal logs", + "internal/format/otlp/settings.go", + "\t\tif s.signal != Traces {", + "\t\tif s.signal != Traces && false {", + "TestOTLPRefusesASettingThatHasNothingToShape", + ), + ( + # The other half of the same guard: a default that arrived is refused + # as if it had been chosen, which is what puts a signal out of reach + # from a window that sends every setting it draws. + "otlp refuses a misplaced setting that was sent at its default", + "internal/format/otlp/settings.go", + "\t\tif !chosen || v == \"\" || v == p.Default {", + "\t\tif !chosen || v == \"\" {", + "TestOTLPRefusesASettingThatHasNothingToShape", + ), + ( + # Go's parser takes a fraction of a second the layout does not have. + "otlp start is not written back and compared, so a fraction of a second passes", + "internal/format/otlp/settings.go", + "\tif err != nil || at.Format(startLayout) != raw {", + "\tif err != nil {", + "TestOTLPRefusesAStartItCannotWrite", + ), + ( + "otlp start is taken from any year", + "internal/format/otlp/settings.go", + "\tif at.Year() < firstYear || at.Year() > lastYear {", + "\tif (at.Year() < firstYear || at.Year() > lastYear) && false {", + "TestOTLPRefusesAStartItCannotWrite", + ), + ( + "an otlp file that runs past the last moment a time can name is planned", + "internal/format/otlp/settings.go", + "; ticks <= room {", + "; ticks <= room || true {", + "TestOTLPRefusesAStartItCannotWrite", + ), + ( + # CPython's json reads NaN and Infinity and writes them back, so a + # value nothing else looks at passes the comparison of the layout. + "the otlp check reads a number that is not JSON without a word", + "internal/oracle/strict.py", + " doc = json.loads(text, parse_constant=otlp_not_json)", + " doc = json.loads(text)", + "TestTheOTLPCheckRefusesWhatAJSONReaderLetsThrough", + ), ] diff --git a/internal/guard/mutationcoverage_test.go b/internal/guard/mutationcoverage_test.go index 7e1196c3..3b9f42df 100644 --- a/internal/guard/mutationcoverage_test.go +++ b/internal/guard/mutationcoverage_test.go @@ -136,6 +136,11 @@ var notProvenByMutation = map[string]bool{ // "proven another way" are different states and lumping them together would // send a later session to re-prove what is already proven. var provenByProbe = map[string]string{ + // Runs only in the CI job that has the Collector, so a mutation job without + // one would meet a skip. What the guard adds to the structural check is the + // oracle itself, and the oracle was handed three files it has to refuse. + "TestEveryOTLPSettingSurvivesItsReferenceTool": "broken on 2026-11-11 by its input, through the same script in Docker with the Collector 0.165.0 (tools/probes/otlp/oracle-in-docker.py): a span with no status member, an enum written as its name and a line of 2 MiB. " + + "The oracle said the first two came back different at the byte where the Collector writes its own spelling, and that 0 of 1 batches came back for the third. Three healthy files passed beside them.", // Reached only where the filesystem folds case and still keeps STRASSE.txt // apart from a sharp s spelling - NTFS does, the Linux the mutation job runs // on keeps every case apart and APFS joins the pair, so no CI mutation of diff --git a/internal/guard/notelemetry_test.go b/internal/guard/notelemetry_test.go index ac0a2d58..29c5a894 100644 --- a/internal/guard/notelemetry_test.go +++ b/internal/guard/notelemetry_test.go @@ -144,6 +144,7 @@ var allowedURLs = map[string]string{ "internal/format/docx/docx.go|http://schemas.openxmlformats.org/officeDocument/2006/relationships": "OPC namespace, written into the document", "internal/format/docx/docx.go|http://schemas.openxmlformats.org/wordprocessingml/2006/main": "WordprocessingML namespace", "internal/format/opc/opc.go|http://schemas.openxmlformats.org/package/2006/content-types": "OPC content types namespace", + "internal/format/otlp/vocabulary.go|https://opentelemetry.io/schemas/1.45.0": "the schema address of the semantic conventions, written into every batch as OTLP asks and never opened", "internal/format/opc/opc.go|http://schemas.openxmlformats.org/package/2006/relationships": "OPC relationships namespace", "internal/format/pptx/pptx.go|http://schemas.openxmlformats.org/drawingml/2006/main": "DrawingML namespace", "internal/format/pptx/pptx.go|http://schemas.openxmlformats.org/officeDocument/2006/relationships": "OPC namespace, written into the document", diff --git a/internal/guard/oracle_test.go b/internal/guard/oracle_test.go index 67172dc6..243e9bea 100644 --- a/internal/guard/oracle_test.go +++ b/internal/guard/oracle_test.go @@ -191,6 +191,11 @@ var structurallyChecked = map[string]bool{ // brace among them, and only this layer knows which fallback each letter // was ordered to carry (docs/RTF-2026-10-15.md section 6.1). "rtf": true, + // Since 2026-11-11, from its first day: the Collector read an enum written + // as a name, keys in snake case and a span that ends before it starts, and + // only this layer knows the batch, the clock and the depth that were + // ordered (docs/OTLP-2026-11-10.md section 3). + "otlp": true, } func TestTheStructuralCheckerCoversEveryFormatItShould(t *testing.T) { diff --git a/internal/guard/otlp_test.go b/internal/guard/otlp_test.go new file mode 100644 index 00000000..f52343ad --- /dev/null +++ b/internal/guard/otlp_test.go @@ -0,0 +1,371 @@ +package guard + +import ( + "bytes" + "context" + "errors" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/donislawdev/TestingFilesGenerator/internal/format" + "github.com/donislawdev/TestingFilesGenerator/internal/format/otlp" + "github.com/donislawdev/TestingFilesGenerator/internal/oracle" +) + +// otlpCases is every setting of the format at both ends of what it takes, and +// the settings that meet inside one record together. A batch of one and a +// trace of one span are the two places where "the first of" and "the last of" +// are the same record. +var otlpCases = []map[string]string{ + {}, + {otlp.Formatting: otlp.Minified}, + {otlp.Formatting: otlp.Indented}, + {otlp.Batch: "1"}, + {otlp.Batch: "2"}, + {otlp.Batch: "1000"}, + {otlp.Depth: "1"}, + {otlp.Depth: "2"}, + {otlp.Depth: "100"}, + {otlp.StatusMix: "success"}, + {otlp.StatusMix: "client-errors"}, + {otlp.StatusMix: "server-errors"}, + {otlp.Rate: "7"}, + {otlp.Rate: "1000000"}, + {otlp.Start: "2002-01-01T00:00:00Z"}, + {otlp.Start: "2199-12-31T23:59:59Z"}, + {otlp.Depth: "100", otlp.Batch: "1", otlp.StatusMix: "server-errors"}, + {otlp.Depth: "1", otlp.Formatting: otlp.Indented, otlp.Rate: "1000"}, + + {otlp.Signal: otlp.Logs}, + {otlp.Signal: otlp.Logs, otlp.Formatting: otlp.Minified}, + {otlp.Signal: otlp.Logs, otlp.Formatting: otlp.Indented}, + {otlp.Signal: otlp.Logs, otlp.LevelMix: "quiet"}, + {otlp.Signal: otlp.Logs, otlp.LevelMix: "errors"}, + {otlp.Signal: otlp.Logs, otlp.LevelMix: "debug"}, + {otlp.Signal: otlp.Logs, otlp.TraceContext: "false"}, + {otlp.Signal: otlp.Logs, otlp.Batch: "3", otlp.Rate: "1000", otlp.LevelMix: "errors", otlp.TraceContext: "false"}, + {otlp.Signal: otlp.Logs, otlp.Batch: "1", otlp.Formatting: otlp.RecordPerLine, otlp.Start: "2199-12-31T23:59:59Z"}, +} + +// otlpSizes are the sizes every case is written at: the smallest file and the +// byte above it, where the padding goes from nothing to one byte, a size of a +// few dozen records, and one large enough to end a default batch and open a +// second. +func otlpSizes(smallest int64) []int64 { return []int64{smallest, smallest + 1, 20000, 300000} } + +// The structural checker is told the settings and has to find the file made +// the way they say: laid out as ordered and byte for byte the text that layout +// makes of its own content, every batch but the last as large as ordered, the +// clock running from start at the rate, every trace as deep as ordered with +// each child inside its parent, the padding on the last record and on no +// other. +// +// It runs wherever python does, which is every machine this suite runs on. +// The Collector beside it runs in one job of CI only, and that is the reason +// this guard is not folded into the one below: a skip there would take this +// check with it. +func TestAnOTLPFileIsWhatItsSettingsSay(t *testing.T) { + d := otlpFormat(t) + dir := t.TempDir() + var reqs []oracle.StrictRequest + batched := 0 + for i, props := range otlpCases { + smallest := d.SmallestAccepted(format.Request{Properties: props}) + for _, size := range otlpSizes(smallest) { + path := filepath.Join(dir, strconv.Itoa(i)+"_"+strconv.FormatInt(size, 10)+".jsonl") + body := writeOTLP(t, path, size, uint64(57+i), props) + reqs = append(reqs, oracle.StrictRequest{Format: "otlp", Path: path, Settings: props}) + if bytes.Count(body, []byte("\n")) > 1 && props[otlp.Formatting] == "" { + batched++ + } + } + } + // One process for all of them: started once for each file, the checker + // took a second a file and this guard two minutes. + got, available, err := oracle.StrictBatch(t.Context(), reqs) + if !available { + t.Skip("the structural check needs python") + } + if err != nil { + t.Fatal(err) + } + for i, j := range got { + if j.Verdict != oracle.StrictOK { + t.Errorf("%v in %s: %s %s", reqs[i].Settings, filepath.Base(reqs[i].Path), j.Verdict, j.Reason) + } + } + // The state this guard exists to check is a file of several batches, where + // a record opens one and another ends one. Asserted rather than assumed. + if len(got) != len(reqs) || batched < len(otlpCases)/2 { + t.Fatalf("%d of %d files judged and %d of them held more than one batch - too few to say anything about a batch that ends", + len(got), len(reqs), batched) + } +} + +// The Collector reads every file and writes it back, and what comes back has +// to be what went in, byte for byte and batch for batch. Reading alone would +// say little: measured on 2026-11-10 the Collector takes spellings the +// specification forbids and drops a line it cannot read without a word +// (docs/OTLP-2026-11-10.md section 3). +// +// Named to match ReferenceTool, because that is the one job in CI with the +// Collector installed. Anywhere else it skips, and says so. +func TestEveryOTLPSettingSurvivesItsReferenceTool(t *testing.T) { + d := otlpFormat(t) + collector := mustOracle(t, d) + dir := t.TempDir() + for i, props := range otlpCases { + smallest := d.SmallestAccepted(format.Request{Properties: props}) + // Three sizes rather than four: the Collector is started for every + // file, and the size left out says nothing the largest does not. + for _, size := range []int64{smallest, smallest + 1, 300000} { + path := filepath.Join(dir, strconv.Itoa(i)+"_"+strconv.FormatInt(size, 10)+".jsonl") + writeOTLP(t, path, size, uint64(57+i), props) + res := collector.Check(path) + if !res.Available { + t.Skipf("%s is not installed, so no file was read by it - a skip, not a pass", res.Tool) + } + if res.Err != nil { + t.Fatalf("%v at %d B: %v", props, size, res.Err) + } + } + } +} + +// The file receiver of the Collector drops a line longer than 1 MiB without a +// word in its log, measured on 2026-11-10: 1 048 576 B was read and 1 048 577 B +// was not. So no setting may make such a line, and the largest batch the +// format allows is what decides that. +// +// Asked two ways. The bound the format works out has to stay under the +// ceiling for the longest records either kind of telemetry can hold, and a +// real file at the largest batch has to stay under the bound - with at least +// one line holding a full batch, or the file never reached the state the +// bound is about. +func TestNoOTLPLineIsLongerThanTheFileReceiverReads(t *testing.T) { + d := otlpFormat(t) + for _, props := range []map[string]string{ + {otlp.Batch: "1000", otlp.Depth: "1", otlp.StatusMix: "server-errors"}, + {otlp.Batch: "1000", otlp.Depth: "100"}, + {otlp.Batch: "1000", otlp.Signal: otlp.Logs}, + {otlp.Batch: "1000", otlp.Signal: otlp.Logs, otlp.LevelMix: "errors"}, + } { + bound, err := otlp.LongestLine(props) + if err != nil { + t.Fatal(err) + } + if bound > otlp.LineCeiling { + t.Errorf("%v: a line can reach %d B and the file receiver reads %d B at most.\n"+ + "Reason: the receiver drops a longer line without a word, so the batches of it would go missing\n"+ + "from a file that is the right size. Lower the largest batch", props, bound, otlp.LineCeiling) + } + p, err := d.Generator.Plan(format.Request{Bytes: 3 << 20, Seed: 4, Properties: props}) + if err != nil { + t.Fatal(err) + } + var buf bytes.Buffer + if err := d.Generator.Write(context.Background(), &buf, p); err != nil { + t.Fatal(err) + } + full := 0 + leaf := map[string]string{"": `{"traceId":`, otlp.Logs: `{"timeUnixNano":`}[props[otlp.Signal]] + for _, line := range strings.Split(strings.TrimSuffix(buf.String(), "\n"), "\n") { + if int64(len(line)) > bound { + t.Errorf("%v: a line of %d B, and the format says none is longer than %d B", props, len(line), bound) + } + if strings.Count(line, leaf) == 1000 { + full++ + } + } + if full == 0 { + t.Errorf("%v: no line of the file holds a full batch, so nothing was measured at the largest one", props) + } + } +} + +// A setting that can do nothing in the file ordered is refused rather than +// ignored: one that shapes spans beside logs, one that shapes log records +// beside traces, and a batch beside a layout that has no batches. The refusal +// names both settings, because naming one of a pair leaves the reader to guess +// which of the two to change. +// +// Only a real choice is refused. A window sends every setting it draws at its +// default, so the same keys at their defaults have to pass - the defect the +// log format had on 2026-08-31, when four of its shapes could not be reached +// from the window at all. +func TestOTLPRefusesASettingThatHasNothingToShape(t *testing.T) { + d := otlpFormat(t) + for _, c := range []struct { + props map[string]string + key string + }{ + {map[string]string{otlp.Signal: otlp.Logs, otlp.Depth: "5"}, "depth and signal"}, + {map[string]string{otlp.Signal: otlp.Logs, otlp.StatusMix: "success"}, "status_mix and signal"}, + {map[string]string{otlp.LevelMix: "errors"}, "level_mix and signal"}, + {map[string]string{otlp.TraceContext: "false"}, "trace_context and signal"}, + {map[string]string{otlp.Formatting: otlp.Minified, otlp.Batch: "10"}, "batch and formatting"}, + {map[string]string{otlp.Formatting: otlp.Indented, otlp.Batch: "10"}, "batch and formatting"}, + } { + _, err := d.Generator.Plan(format.Request{Bytes: 100000, Seed: 1, Properties: c.props}) + var refused *format.PropertyValueError + if !errors.As(err, &refused) { + t.Errorf("%v was planned, and one of those settings does nothing beside the other: %v", c.props, err) + continue + } + if refused.Key != c.key { + t.Errorf("%v was refused over %q, not over %q", c.props, refused.Key, c.key) + } + } + + everyDefault := map[string]string{} + for _, p := range d.Properties { + everyDefault[p.Name] = p.Default + } + for _, signal := range []string{otlp.Traces, otlp.Logs} { + everyDefault[otlp.Signal] = signal + for _, layout := range []string{otlp.RecordPerLine, otlp.Minified, otlp.Indented} { + everyDefault[otlp.Formatting] = layout + if _, err := d.Generator.Plan(format.Request{Bytes: 100000, Seed: 1, Properties: everyDefault}); err != nil { + t.Errorf("signal %s laid out %s with every other setting sent at its default was refused: %v.\n"+ + "Reason: a window sends every setting it draws, so a default that arrived is not a choice", + signal, layout, err) + } + } + } +} + +// start takes one spelling of a moment, in the years where a time in +// nanoseconds has nineteen digits, and a file may not run past the last moment +// such a count can name. Each of them is refused with the setting named, +// rather than written as a time one digit longer than planned - which would +// make the file the wrong size somewhere near its end. +func TestOTLPRefusesAStartItCannotWrite(t *testing.T) { + d := otlpFormat(t) + for _, start := range []string{ + "2026-08-01", "2026-08-01T12:00:00", "2026-08-01T12:00:00+02:00", "2026-08-01T12:00:00.5Z", + "2026-02-30T12:00:00Z", "2026-08-01 12:00:00Z", "yesterday", + "2001-12-31T23:59:59Z", "2200-01-01T00:00:00Z", "1970-01-01T00:00:00Z", + } { + _, err := d.Generator.Plan(format.Request{Bytes: 100000, Seed: 1, Properties: map[string]string{otlp.Start: start}}) + var refused *format.PropertyValueError + if !errors.As(err, &refused) || refused.Key != otlp.Start { + t.Errorf("start=%q was not refused over start: %v", start, err) + } + } + + // Hundreds of gigabytes at one record a second, from the last year a file + // may start in. Planned only: nothing is written. + late := map[string]string{otlp.Start: "2199-12-31T23:59:59Z", otlp.Signal: otlp.Logs} + _, err := d.Generator.Plan(format.Request{Bytes: 400 << 30, Seed: 1, Properties: late}) + var refused *format.PropertyValueError + if !errors.As(err, &refused) || refused.Key != otlp.Start { + t.Errorf("a file of 400 GiB from %s at one record a second was not refused over start: %v", late[otlp.Start], err) + } + late[otlp.Rate] = "1000000" + if _, err := d.Generator.Plan(format.Request{Bytes: 400 << 30, Seed: 1, Properties: late}); err != nil { + t.Errorf("the same file at a million records a second ends in time and was refused: %v", err) + } +} + +// CPython's json reads NaN and Infinity, which no JSON grammar has, and +// writes them back, so one of them in a value nothing else looked at passed +// the comparison of the layout and the file was called sound. A document +// nested past what the decoder walks ended the checker with an exception, +// and so did a rate of nought handed to it. All three were found by the +// review of #230 and measured on the checker as it stood: OK, a traceback, a +// traceback. +// +// Each has to be a refusal with a reason. A crash also turns a guard red, and +// a guard asking whether a damaged file is refused must not count the +// checker falling over as a refusal. +func TestTheOTLPCheckRefusesWhatAJSONReaderLetsThrough(t *testing.T) { + dir := t.TempDir() + good := writeOTLP(t, filepath.Join(dir, "good.jsonl"), 4096, 9, map[string]string{otlp.Batch: "1"}) + service := []byte(`{"stringValue":"shop"}`) + if bytes.Count(good, service) == 0 { + t.Fatal("the file names no service, so there is nowhere to put a number that is not JSON") + } + write := func(name string, body []byte) string { + path := filepath.Join(dir, name) + if err := os.WriteFile(path, body, 0o600); err != nil { + t.Fatal(err) + } + return path + } + deep := append(bytes.Repeat([]byte("["), 200000), bytes.Repeat([]byte("]"), 200000)...) + batch := map[string]string{otlp.Batch: "1"} + // because is what the refusal has to say. Refused is not enough: the + // checker has later checks, and a file let through by the first one can + // be refused by another for a different reason. Measured on 2026-11-11 - + // with NaN read quietly, the mutation run on #230 found the file still + // refused, by a later check, and reported the entry as a hole. + cases := []struct { + what string + because string + req oracle.StrictRequest + }{ + {"NaN where the name of the service stands", "NaN is not a JSON number", oracle.StrictRequest{Format: "otlp", Settings: batch, + Path: write("nan.jsonl", bytes.Replace(good, service, []byte(`{"doubleValue":NaN}`), 1))}}, + {"Infinity in the same place", "Infinity is not a JSON number", oracle.StrictRequest{Format: "otlp", Settings: batch, + Path: write("infinity.jsonl", bytes.Replace(good, service, []byte(`{"doubleValue":Infinity}`), 1))}}, + {"a document nested 200000 deep", "nested deeper than a JSON reader goes", oracle.StrictRequest{Format: "otlp", Settings: batch, Path: write("deep.jsonl", append(deep, '\n'))}}, + {"a rate of nought", "rate='0'", oracle.StrictRequest{Format: "otlp", Path: filepath.Join(dir, "good.jsonl"), + Settings: map[string]string{otlp.Batch: "1", otlp.Rate: "0"}}}, + {"a depth that is a word", "depth='deep'", oracle.StrictRequest{Format: "otlp", Path: filepath.Join(dir, "good.jsonl"), + Settings: map[string]string{otlp.Batch: "1", otlp.Depth: "deep"}}}, + } + reqs := []oracle.StrictRequest{{Format: "otlp", Path: filepath.Join(dir, "good.jsonl"), Settings: batch}} + for _, c := range cases { + reqs = append(reqs, c.req) + } + got, available, err := oracle.StrictBatch(t.Context(), reqs) + if !available { + t.Skip("the structural check needs python") + } + if err != nil { + t.Fatal(err) + } + if got[0].Verdict != oracle.StrictOK { + t.Fatalf("the file every case is made from is itself refused, so no refusal below says anything: %s", got[0].Reason) + } + for i, c := range cases { + if j := got[i+1]; j.Verdict != oracle.StrictFail { + t.Errorf("%s: the checker answered %s (%s), and it has to refuse with a reason", c.what, j.Verdict, j.Reason) + } else if !strings.Contains(j.Reason, c.because) { + t.Errorf("%s: refused, but for another reason - %q, where it has to say %q", c.what, j.Reason, c.because) + } + } +} + +func otlpFormat(t *testing.T) format.Descriptor { + t.Helper() + d, err := format.Get("otlp") + if err != nil { + t.Fatal(err) + } + return d +} + +// writeOTLP writes one file of exactly size bytes and returns what it holds. +func writeOTLP(t *testing.T, path string, size int64, seed uint64, props map[string]string) []byte { + t.Helper() + d := otlpFormat(t) + p, err := d.Generator.Plan(format.Request{Bytes: size, Seed: seed, Label: true, Properties: props}) + if err != nil { + t.Fatalf("planning %d B with %v: %v", size, props, err) + } + var buf bytes.Buffer + if err := d.Generator.Write(context.Background(), &buf, p); err != nil { + t.Fatalf("writing %d B with %v: %v", size, props, err) + } + if int64(buf.Len()) != size { + t.Fatalf("%v: asked for %d B and got %d", props, size, buf.Len()) + } + if err := os.WriteFile(path, buf.Bytes(), 0o600); err != nil { + t.Fatal(err) + } + return buf.Bytes() +} diff --git a/internal/guard/parity_test.go b/internal/guard/parity_test.go index 27b19474..90afd5c6 100644 --- a/internal/guard/parity_test.go +++ b/internal/guard/parity_test.go @@ -129,6 +129,7 @@ var reachableFromTheWindow = []string{ "format:rtf", "format:json", "format:geojson", + "format:otlp", "format:eml", "format:log", "format:avif", @@ -202,6 +203,15 @@ var reachableFromTheWindow = []string{ "property:geojson.unlocated", "property:geojson.vertices", "property:geojson.winding", + "property:otlp.batch", + "property:otlp.depth", + "property:otlp.formatting", + "property:otlp.level_mix", + "property:otlp.rate", + "property:otlp.signal", + "property:otlp.start", + "property:otlp.status_mix", + "property:otlp.trace_context", "property:gif.frames", "property:gif.height", "property:gif.width", diff --git a/internal/guard/presetbytes_test.go b/internal/guard/presetbytes_test.go index e3d4b6c5..f94b599a 100644 --- a/internal/guard/presetbytes_test.go +++ b/internal/guard/presetbytes_test.go @@ -113,7 +113,12 @@ func TestEjectingAPresetGivesTheBytesItAlwaysGave(t *testing.T) { // rtf is a format more, so the set has its smallest document, 65 B - one // target, minimal_rtf, and nothing else in the recipe moves // (docs/RTF-2026-10-15.md). - {id: "empty-and-minimal", args: preset.Args{}, bytes: 10497, sum: "0853827d3b55354dbe34c80025e0f298731522704218ce09801dda90f8e04034"}, + // Moved a ninth time on 2026-11-11, under the decision of 2026-10-06 and + // said to the owner before the code this time: otlp is a format more, so + // the set has its smallest file of spans, 1266 B - one target, + // minimal_otlp, which stands after mp4, so the files after it in the run + // take the next id (docs/OTLP-2026-11-10.md section 7). + {id: "empty-and-minimal", args: preset.Args{}, bytes: 10821, sum: "8ca10156851379fae52dbf52fc220fd7470d0656b2f2e4ebc5f18af2a90dc7c2"}, {id: "empty-and-minimal", args: preset.Args{"formats": "jpg,png,txt"}, bytes: 1472, sum: "19b12800372395ad3bf5cba6407ca654d7ed51eba3a5bc5436f76a94bc09253c"}, // The preset of unusual file names, measured 2026-09-25 on its first // build: the default, and a format whose extension is a byte longer, diff --git a/internal/guard/testdata/generator-golden.json b/internal/guard/testdata/generator-golden.json index 1f217a3f..4c581b6c 100644 --- a/internal/guard/testdata/generator-golden.json +++ b/internal/guard/testdata/generator-golden.json @@ -680,6 +680,51 @@ "bytes": 16384, "sha256": "aafebff3937203366d88a1af0a14d555176e683ded8cffa67ac05bddafc13733", "measured_on": "2026-10-27" + }, + "otlp_8kib": { + "bytes": 8192, + "sha256": "df2e261aec68a2ed4a40bec4d1c5f4910c436b2c8dbcc72375d4468a00766798", + "measured_on": "2026-11-11" + }, + "otlp_8kib_minified": { + "bytes": 8192, + "sha256": "0ea08f4a0ba951f7b12dacee15d9fa63274bdb9c84da2ab25405d82e0604a587", + "measured_on": "2026-11-11" + }, + "otlp_8kib_indented": { + "bytes": 8192, + "sha256": "c6e5389ae8a2c94fa1682d768e541619258ec230dee9293b46f3a54a3c9fda9b", + "measured_on": "2026-11-11" + }, + "otlp_8kib_batches": { + "bytes": 8192, + "sha256": "4bd64f62551289b04f04822d01993d576180fc6fc89bb3609dc99e928ea38f75", + "measured_on": "2026-11-11" + }, + "otlp_8kib_single_spans": { + "bytes": 8192, + "sha256": "a383c2434cfaf64e616e328628d6e906d92b86351f1b1f37ca60e646500a9e89", + "measured_on": "2026-11-11" + }, + "otlp_8kib_deep_failures": { + "bytes": 8192, + "sha256": "c95cd30a73791c48aacf9933e4006ad05f5cfaad7ca635317b4464b451cfe2ef", + "measured_on": "2026-11-11" + }, + "otlp_8kib_logs": { + "bytes": 8192, + "sha256": "27e256d1c377b03c7ac7fab28f2a1a1cd7c9421759e6b61222429fa030b18a2b", + "measured_on": "2026-11-11" + }, + "otlp_8kib_logs_batches": { + "bytes": 8192, + "sha256": "7c5d7d8721ff3de864ba8bc30a4df57c6150534ad2bb61446d9e24a3292a0a4f", + "measured_on": "2026-11-11" + }, + "otlp_8kib_logs_indented_no_trace": { + "bytes": 8192, + "sha256": "1380382a49470452d1662988429244b75a801dd50b8b7d0af21f773aee32f62d", + "measured_on": "2026-11-11" } }, "remeasured": [ diff --git a/internal/guard/testdata/screens/catalogue.png b/internal/guard/testdata/screens/catalogue.png index 67dca6ff..e3c42f71 100644 Binary files a/internal/guard/testdata/screens/catalogue.png and b/internal/guard/testdata/screens/catalogue.png differ diff --git a/internal/guard/testdata/screens/catalogue.xml b/internal/guard/testdata/screens/catalogue.xml index a1f1e67f..9b30c995 100644 --- a/internal/guard/testdata/screens/catalogue.xml +++ b/internal/guard/testdata/screens/catalogue.xml @@ -1338,7 +1338,7 @@ - + Archives · 2 @@ -1347,13 +1347,13 @@ targz - tar + gzip + tar + gzip zip - ZIP + ZIP @@ -1363,31 +1363,31 @@ docx - Word (Office Open XML) + Word (Office Open XML) pdf - Portable Document Format + Portable Document Format pptx - PowerPoint (Office Open XML) + PowerPoint (Office Open XML) rtf - Rich Text Format + Rich Text Format xlsx - Excel (Office Open XML) + Excel (Office Open XML) @@ -1397,7 +1397,7 @@ eml - Email message + Email message @@ -1407,62 +1407,62 @@ avif - AV1 Image File Format + AV1 Image File Format bmp - Windows Bitmap + Windows Bitmap gif - Graphics Interchange Format + Graphics Interchange Format ico - Windows Icon + Windows Icon jpg - JPEG + JPEG jxl - JPEG XL + JPEG XL png - Portable Network Graphics + Portable Network Graphics svg - Scalable Vector Graphics + Scalable Vector Graphics tiff - Tagged Image File Format + Tagged Image File Format webp - WebP + WebP @@ -1472,102 +1472,108 @@ wav - Waveform Audio + Waveform Audio - Text and data · 10 + Text and data · 11 csv - Comma-Separated Values + Comma-Separated Values geojson - GeoJSON + GeoJSON html - HyperText Markup Language + HyperText Markup Language json - JavaScript Object Notation + JavaScript Object Notation log - Server and application log + Server and application log md - Markdown + Markdown - toml - TOML + otlp + OpenTelemetry Protocol JSON - txt - Plain text + toml + TOML - xml - Extensible Markup Language + txt + Plain text - yaml - YAML + xml + Extensible Markup Language - Video · 2 + + yaml + YAML + + Video · 2 + + mp4 - MP4 video + MP4 video - + webm - WebM video + WebM video - - + + - + @@ -1604,7 +1610,7 @@ - + Archives · 1 @@ -1615,7 +1621,7 @@ zi p - ZIP + ZIP @@ -1649,7 +1655,7 @@ avif - AV1 Image File Format + AV1 Image File Format @@ -1657,19 +1663,19 @@ bm p - Windows Bitmap + Windows Bitmap gif - Graphics Interchange Format + Graphics Interchange Format ico - Windows Icon + Windows Icon @@ -1677,13 +1683,13 @@ j p g - JPEG + JPEG jxl - JPEG XL + JPEG XL @@ -1700,13 +1706,13 @@ svg - Scalable Vector Graphics + Scalable Vector Graphics tiff - Tagged Image File Format + Tagged Image File Format @@ -1714,13 +1720,23 @@ web p - WebP + WebP - Text and data · 1 + Text and data · 2 + + + otl + p + + OpenTelemetry + P + rotocol JSON + + txt @@ -1728,32 +1744,32 @@ P lain text - + Video · 1 - + m p 4 - MP4 video + MP4 video - - + + - + @@ -1797,7 +1813,7 @@ - + diff --git a/internal/guard/testdata/screens/generate-menu-hovered.png b/internal/guard/testdata/screens/generate-menu-hovered.png index 6944fa71..3eaad35f 100644 Binary files a/internal/guard/testdata/screens/generate-menu-hovered.png and b/internal/guard/testdata/screens/generate-menu-hovered.png differ diff --git a/internal/guard/testdata/screens/generate-menu-hovered.xml b/internal/guard/testdata/screens/generate-menu-hovered.xml index bb2351d8..c03b5ce4 100644 --- a/internal/guard/testdata/screens/generate-menu-hovered.xml +++ b/internal/guard/testdata/screens/generate-menu-hovered.xml @@ -511,7 +511,7 @@ - + Archives · 2 @@ -520,13 +520,13 @@ targz - tar + gzip + tar + gzip zip - ZIP + ZIP @@ -536,31 +536,31 @@ docx - Word (Office Open XML) + Word (Office Open XML) pdf - Portable Document Format + Portable Document Format pptx - PowerPoint (Office Open XML) + PowerPoint (Office Open XML) rtf - Rich Text Format + Rich Text Format xlsx - Excel (Office Open XML) + Excel (Office Open XML) @@ -570,7 +570,7 @@ eml - Email message + Email message @@ -581,61 +581,61 @@ avif - AV1 Image File Format + AV1 Image File Format bmp - Windows Bitmap + Windows Bitmap gif - Graphics Interchange Format + Graphics Interchange Format ico - Windows Icon + Windows Icon jpg - JPEG + JPEG jxl - JPEG XL + JPEG XL png - Portable Network Graphics + Portable Network Graphics svg - Scalable Vector Graphics + Scalable Vector Graphics tiff - Tagged Image File Format + Tagged Image File Format webp - WebP + WebP @@ -645,102 +645,108 @@ wav - Waveform Audio + Waveform Audio - Text and data · 10 + Text and data · 11 csv - Comma-Separated Values + Comma-Separated Values geojson - GeoJSON + GeoJSON html - HyperText Markup Language + HyperText Markup Language json - JavaScript Object Notation + JavaScript Object Notation log - Server and application log + Server and application log md - Markdown + Markdown - toml - TOML + otlp + OpenTelemetry Protocol JSON - txt - Plain text + toml + TOML - xml - Extensible Markup Language + txt + Plain text - yaml - YAML + xml + Extensible Markup Language - Video · 2 + + yaml + YAML + + Video · 2 + + mp4 - MP4 video + MP4 video - + webm - WebM video + WebM video - - + + - + diff --git a/internal/guard/testdata/screens/generate-menu-keyed.png b/internal/guard/testdata/screens/generate-menu-keyed.png index 1bf933ae..bbad9a8d 100644 Binary files a/internal/guard/testdata/screens/generate-menu-keyed.png and b/internal/guard/testdata/screens/generate-menu-keyed.png differ diff --git a/internal/guard/testdata/screens/generate-menu-keyed.xml b/internal/guard/testdata/screens/generate-menu-keyed.xml index 1d178eb1..b7e48837 100644 --- a/internal/guard/testdata/screens/generate-menu-keyed.xml +++ b/internal/guard/testdata/screens/generate-menu-keyed.xml @@ -511,7 +511,7 @@ - + Archives · 2 @@ -520,13 +520,13 @@ targz - tar + gzip + tar + gzip zip - ZIP + ZIP @@ -536,31 +536,31 @@ docx - Word (Office Open XML) + Word (Office Open XML) pdf - Portable Document Format + Portable Document Format pptx - PowerPoint (Office Open XML) + PowerPoint (Office Open XML) rtf - Rich Text Format + Rich Text Format xlsx - Excel (Office Open XML) + Excel (Office Open XML) @@ -570,7 +570,7 @@ eml - Email message + Email message @@ -581,61 +581,61 @@ avif - AV1 Image File Format + AV1 Image File Format bmp - Windows Bitmap + Windows Bitmap gif - Graphics Interchange Format + Graphics Interchange Format ico - Windows Icon + Windows Icon jpg - JPEG + JPEG jxl - JPEG XL + JPEG XL png - Portable Network Graphics + Portable Network Graphics svg - Scalable Vector Graphics + Scalable Vector Graphics tiff - Tagged Image File Format + Tagged Image File Format webp - WebP + WebP @@ -645,102 +645,108 @@ wav - Waveform Audio + Waveform Audio - Text and data · 10 + Text and data · 11 csv - Comma-Separated Values + Comma-Separated Values geojson - GeoJSON + GeoJSON html - HyperText Markup Language + HyperText Markup Language json - JavaScript Object Notation + JavaScript Object Notation log - Server and application log + Server and application log md - Markdown + Markdown - toml - TOML + otlp + OpenTelemetry Protocol JSON - txt - Plain text + toml + TOML - xml - Extensible Markup Language + txt + Plain text - yaml - YAML + xml + Extensible Markup Language - Video · 2 + + yaml + YAML + + Video · 2 + + mp4 - MP4 video + MP4 video - + webm - WebM video + WebM video - - + + - + diff --git a/internal/guard/testdata/screens/generate-menu.png b/internal/guard/testdata/screens/generate-menu.png index f94e0623..706308b0 100644 Binary files a/internal/guard/testdata/screens/generate-menu.png and b/internal/guard/testdata/screens/generate-menu.png differ diff --git a/internal/guard/testdata/screens/generate-menu.xml b/internal/guard/testdata/screens/generate-menu.xml index 9f0d5027..c3aaa886 100644 --- a/internal/guard/testdata/screens/generate-menu.xml +++ b/internal/guard/testdata/screens/generate-menu.xml @@ -511,7 +511,7 @@ - + Archives · 2 @@ -520,13 +520,13 @@ targz - tar + gzip + tar + gzip zip - ZIP + ZIP @@ -536,31 +536,31 @@ docx - Word (Office Open XML) + Word (Office Open XML) pdf - Portable Document Format + Portable Document Format pptx - PowerPoint (Office Open XML) + PowerPoint (Office Open XML) rtf - Rich Text Format + Rich Text Format xlsx - Excel (Office Open XML) + Excel (Office Open XML) @@ -570,7 +570,7 @@ eml - Email message + Email message @@ -581,61 +581,61 @@ avif - AV1 Image File Format + AV1 Image File Format bmp - Windows Bitmap + Windows Bitmap gif - Graphics Interchange Format + Graphics Interchange Format ico - Windows Icon + Windows Icon jpg - JPEG + JPEG jxl - JPEG XL + JPEG XL png - Portable Network Graphics + Portable Network Graphics svg - Scalable Vector Graphics + Scalable Vector Graphics tiff - Tagged Image File Format + Tagged Image File Format webp - WebP + WebP @@ -645,102 +645,108 @@ wav - Waveform Audio + Waveform Audio - Text and data · 10 + Text and data · 11 csv - Comma-Separated Values + Comma-Separated Values geojson - GeoJSON + GeoJSON html - HyperText Markup Language + HyperText Markup Language json - JavaScript Object Notation + JavaScript Object Notation log - Server and application log + Server and application log md - Markdown + Markdown - toml - TOML + otlp + OpenTelemetry Protocol JSON - txt - Plain text + toml + TOML - xml - Extensible Markup Language + txt + Plain text - yaml - YAML + xml + Extensible Markup Language - Video · 2 + + yaml + YAML + + Video · 2 + + mp4 - MP4 video + MP4 video - + webm - WebM video + WebM video - - + + - + diff --git a/internal/guard/testdata/screens/preset-menu-setting.png b/internal/guard/testdata/screens/preset-menu-setting.png index db75b8d6..87310458 100644 Binary files a/internal/guard/testdata/screens/preset-menu-setting.png and b/internal/guard/testdata/screens/preset-menu-setting.png differ diff --git a/internal/guard/testdata/screens/preset-menu-setting.xml b/internal/guard/testdata/screens/preset-menu-setting.xml index bf666242..3cb736e7 100644 --- a/internal/guard/testdata/screens/preset-menu-setting.xml +++ b/internal/guard/testdata/screens/preset-menu-setting.xml @@ -474,7 +474,7 @@ - + Archives · 2 @@ -483,13 +483,13 @@ targz - tar + gzip + tar + gzip zip - ZIP + ZIP @@ -499,32 +499,32 @@ docx - Word (Office Open XML) + Word (Office Open XML) pdf - Portable Document Format + Portable Document Format pptx - PowerPoint (Office Open XML) + PowerPoint (Office Open XML) rtf - Rich Text Format + Rich Text Format xlsx - Excel (Office Open XML) + Excel (Office Open XML) @@ -534,7 +534,7 @@ eml - Email message + Email message @@ -544,61 +544,61 @@ avif - AV1 Image File Format + AV1 Image File Format bmp - Windows Bitmap + Windows Bitmap gif - Graphics Interchange Format + Graphics Interchange Format ico - Windows Icon + Windows Icon jpg - JPEG + JPEG jxl - JPEG XL + JPEG XL png - Portable Network Graphics + Portable Network Graphics svg - Scalable Vector Graphics + Scalable Vector Graphics tiff - Tagged Image File Format + Tagged Image File Format webp - WebP + WebP @@ -608,102 +608,108 @@ wav - Waveform Audio + Waveform Audio - Text and data · 10 + Text and data · 11 csv - Comma-Separated Values + Comma-Separated Values geojson - GeoJSON + GeoJSON html - HyperText Markup Language + HyperText Markup Language json - JavaScript Object Notation + JavaScript Object Notation log - Server and application log + Server and application log md - Markdown + Markdown - toml - TOML + otlp + OpenTelemetry Protocol JSON - txt - Plain text + toml + TOML - xml - Extensible Markup Language + txt + Plain text - yaml - YAML + xml + Extensible Markup Language - Video · 2 + + yaml + YAML + + Video · 2 + + mp4 - MP4 video + MP4 video - + webm - WebM video + WebM video - - + + - + diff --git a/internal/guard/testdata/screens/preset-menu.png b/internal/guard/testdata/screens/preset-menu.png index 6dbda3f1..6ef36ed2 100644 Binary files a/internal/guard/testdata/screens/preset-menu.png and b/internal/guard/testdata/screens/preset-menu.png differ diff --git a/internal/guard/testdata/screens/preset-menu.xml b/internal/guard/testdata/screens/preset-menu.xml index 1a8363fe..00037a19 100644 --- a/internal/guard/testdata/screens/preset-menu.xml +++ b/internal/guard/testdata/screens/preset-menu.xml @@ -370,7 +370,7 @@ - 33 files · 39.5 KB (40 496 B) · 31 formats · will go to /tfg/out + 34 files · 40.8 KB (41 762 B) · 32 formats · will go to /tfg/out diff --git a/internal/guard/testdata/screens/preset.png b/internal/guard/testdata/screens/preset.png index 2cd7cc33..f952f873 100644 Binary files a/internal/guard/testdata/screens/preset.png and b/internal/guard/testdata/screens/preset.png differ diff --git a/internal/guard/testdata/screens/preset.xml b/internal/guard/testdata/screens/preset.xml index 199b8b2d..81342575 100644 --- a/internal/guard/testdata/screens/preset.xml +++ b/internal/guard/testdata/screens/preset.xml @@ -370,7 +370,7 @@ - 33 files · 39.5 KB (40 496 B) · 31 formats · will go to /tfg/out + 34 files · 40.8 KB (41 762 B) · 32 formats · will go to /tfg/out diff --git a/internal/guard/testdata/screens/recipe-on-a-preset.png b/internal/guard/testdata/screens/recipe-on-a-preset.png index 92f39378..32cf9fb9 100644 Binary files a/internal/guard/testdata/screens/recipe-on-a-preset.png and b/internal/guard/testdata/screens/recipe-on-a-preset.png differ diff --git a/internal/guard/testdata/screens/recipe-on-a-preset.xml b/internal/guard/testdata/screens/recipe-on-a-preset.xml index 1bc4a9de..691ba72c 100644 --- a/internal/guard/testdata/screens/recipe-on-a-preset.xml +++ b/internal/guard/testdata/screens/recipe-on-a-preset.xml @@ -658,7 +658,7 @@ - 34 files · 10.0 MB (10 526 256 B) · 31 formats · will go to /tfg/out + 35 files · 10.0 MB (10 527 522 B) · 32 formats · will go to /tfg/out diff --git a/internal/guard/textformats_test.go b/internal/guard/textformats_test.go index 80c3080f..cd667ba4 100644 --- a/internal/guard/textformats_test.go +++ b/internal/guard/textformats_test.go @@ -444,7 +444,7 @@ func TestAnSVGDrawingCarriesRealShapes(t *testing.T) { // This is the shape docs/OBSERVATIONS.md now calls out: an audit of // completeness has to run FROM THE SOURCE towards the list. Walking the entries // already written down cannot, by construction, find what is missing from them. -var textFormats = []string{"txt", "md", "log", "csv", "json", "xml", "html", "svg", "yaml", "toml", "geojson", "eml", "rtf"} +var textFormats = []string{"txt", "md", "log", "csv", "json", "xml", "html", "svg", "yaml", "toml", "geojson", "eml", "rtf", "otlp"} var binaryFormats = []string{"avif", "bmp", "docx", "gif", "ico", "jpg", "jxl", "mp4", "pdf", "png", "pptx", "targz", "tiff", "wav", "webm", "webp", "xlsx", "zip"} diff --git a/internal/gui/parts/filekind.go b/internal/gui/parts/filekind.go index cab583b5..25f34a3c 100644 --- a/internal/gui/parts/filekind.go +++ b/internal/gui/parts/filekind.go @@ -169,6 +169,7 @@ var fileKinds = map[string]fileKind{ "json": kindWords, "log": kindWords, "md": kindWords, + "otlp": kindWords, "toml": kindWords, "txt": kindWords, "xml": kindWords, diff --git a/internal/gui/text/locale/registry/en.json b/internal/gui/text/locale/registry/en.json index 49fe5976..ce9ed2f1 100644 --- a/internal/gui/text/locale/registry/en.json +++ b/internal/gui/text/locale/registry/en.json @@ -634,6 +634,11 @@ "hash": "sha256-862c4ec62def", "other": "mp4" }, + "Choice.format.otlp": { + "description": "One entry of the format list, as the list shows it. A recipe and the command line write the value itself, so a token such as utf-8, a4, sha256 or 644 stays as it is.", + "hash": "sha256-e81fa7c0883f", + "other": "otlp" + }, "Choice.format.pdf": { "description": "One entry of the format list, as the list shows it. A recipe and the command line write the value itself, so a token such as utf-8, a4, sha256 or 644 stays as it is.", "hash": "sha256-c35b21d6ca39", @@ -1044,6 +1049,16 @@ "hash": "sha256-69f59c273b6e", "other": "90" }, + "Choice.signal.logs": { + "description": "One entry of the signal list, as the list shows it. A recipe and the command line write the value itself, so a token such as utf-8, a4, sha256 or 644 stays as it is.", + "hash": "sha256-98f38f12db22", + "other": "logs" + }, + "Choice.signal.traces": { + "description": "One entry of the signal list, as the list shows it. A recipe and the command line write the value itself, so a token such as utf-8, a4, sha256 or 644 stays as it is.", + "hash": "sha256-b5ac69fc467c", + "other": "traces" + }, "Choice.status_mix.client-errors": { "description": "One entry of the status_mix list, as the list shows it. A recipe and the command line write the value itself, so a token such as utf-8, a4, sha256 or 644 stays as it is.", "hash": "sha256-cc47f4e83c84", @@ -1569,6 +1584,51 @@ "hash": "sha256-3085ac44f0e0", "other": "How wide the picture is. Left out, a size is chosen that fits the bytes you asked for." }, + "Detail.format/otlp.batch": { + "description": "The sentence under the batch setting of the otlp format, after what it takes.", + "hash": "sha256-3c2c865d96f5", + "other": "How many spans or log records each line holds. 512 is the batch an OpenTelemetry SDK sends by default. Only record-per-line has batches, and beside another layout a batch is refused." + }, + "Detail.format/otlp.depth": { + "description": "The sentence under the depth setting of the otlp format, after what it takes.", + "hash": "sha256-d1caceabbb7a", + "other": "How many spans each trace has, each one the child of the one before it. 1 leaves every span without a parent. It shapes spans, and beside signal logs it is refused." + }, + "Detail.format/otlp.formatting": { + "description": "The sentence under the formatting setting of the otlp format, after what it takes.", + "hash": "sha256-a0bda4ebdc96", + "other": "How the file is laid out. record-per-line puts one batch on each line, which is what the OTLP file exporter writes and the file receiver of the OpenTelemetry Collector reads. minified and indented make the whole file one request, the body to post to an OTLP endpoint, and a file of that kind is one JSON document whatever its name ends in. Posted as a body, a file of several lines loses every line after the first in the Collector, and its file receiver reads nothing from an indented file." + }, + "Detail.format/otlp.level_mix": { + "description": "The sentence under the level_mix setting of the otlp format, after what it takes.", + "hash": "sha256-96679b162c65", + "other": "Which severities the log records carry. Realistic is mostly INFO with some of the others. It shapes log records, and beside signal traces it is refused." + }, + "Detail.format/otlp.rate": { + "description": "The sentence under the rate setting of the otlp format, after what it takes.", + "hash": "sha256-ce402f8f7753", + "other": "How many traces or log records begin each second." + }, + "Detail.format/otlp.signal": { + "description": "The sentence under the signal setting of the otlp format, after what it takes.", + "hash": "sha256-a0541e2d1349", + "other": "Which kind of telemetry the file holds. traces writes spans and logs writes log records. A file holds one kind, as the OTLP file format asks." + }, + "Detail.format/otlp.start": { + "description": "The sentence under the start setting of the otlp format, after what it takes.", + "hash": "sha256-a8823ab3b44e", + "other": "The moment the first span or log record is dated. The later ones follow it at the rate. Change it when the system that receives the file refuses or hides data that is too old." + }, + "Detail.format/otlp.status_mix": { + "description": "The sentence under the status_mix setting of the otlp format, after what it takes.", + "hash": "sha256-06462e0e2639", + "other": "Which response codes the requests end with. A code of 500 or above marks its span as an error, as the HTTP conventions of OpenTelemetry ask, and every other code leaves the status unset. It shapes spans, and beside signal logs it is refused." + }, + "Detail.format/otlp.trace_context": { + "description": "The sentence under the trace_context setting of the otlp format, after what it takes.", + "hash": "sha256-634b48d43770", + "other": "Whether every log record names the trace and the span it was written in. The file holds no spans, so those ids match nothing in it. It shapes log records, and beside signal traces it is refused." + }, "Detail.format/pdf.author": { "description": "The sentence under the author setting of the pdf format, after what it takes.", "hash": "sha256-33b1ee363d86", @@ -2049,6 +2109,11 @@ "hash": "sha256-1f5265d4bfbb", "other": "MP4 video" }, + "Format.otlp": { + "description": "What the otlp format is called, beside its id in a list of formats. The name of a standard stays as it is - only a name that describes the file is translated.", + "hash": "sha256-3aecb4b59e76", + "other": "OpenTelemetry Protocol JSON" + }, "Format.pdf": { "description": "What the pdf format is called, beside its id in a list of formats. The name of a standard stays as it is - only a name that describes the file is translated.", "hash": "sha256-ccf9cd24dc07", @@ -2269,6 +2334,11 @@ "hash": "sha256-d95082a2ee57", "other": "Author" }, + "Label.batch": { + "description": "The name beside the box of a setting a recipe writes as batch.", + "hash": "sha256-320b5d80f8cf", + "other": "Batch" + }, "Label.bbox": { "description": "The name beside the box of a setting a recipe writes as bbox.", "hash": "sha256-708f44b1bd98", @@ -2669,6 +2739,11 @@ "hash": "sha256-218e0d43275e", "other": "Sample rate" }, + "Label.signal": { + "description": "The name beside the box of a setting a recipe writes as signal.", + "hash": "sha256-1e9806e4227b", + "other": "Signal" + }, "Label.slides": { "description": "The name beside the box of a setting a recipe writes as slides.", "hash": "sha256-a0ffe3818444", @@ -2679,6 +2754,11 @@ "hash": "sha256-b11fb1f593a8", "other": "Spread" }, + "Label.start": { + "description": "The name beside the box of a setting a recipe writes as start.", + "hash": "sha256-e4bb9f1ece9a", + "other": "Start" + }, "Label.status_mix": { "description": "The name beside the box of a setting a recipe writes as status_mix.", "hash": "sha256-e5e3006b5ecd", @@ -2714,6 +2794,11 @@ "hash": "sha256-7e8cd2056da7", "other": "Title" }, + "Label.trace_context": { + "description": "The name beside the box of a setting a recipe writes as trace_context.", + "hash": "sha256-07b45d3adabb", + "other": "Trace context" + }, "Label.unlocated": { "description": "The name beside the box of a setting a recipe writes as unlocated.", "hash": "sha256-9d9b671e4837", @@ -2814,6 +2899,11 @@ "hash": "sha256-a40bd1decdc9", "other": "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none" }, + "Shape.a moment in UTC such as 2026-08-01T12:00:00Z": { + "description": "What the text in a box has to look like, in the sentence under it.", + "hash": "sha256-97f0219d13b4", + "other": "a moment in UTC such as 2026-08-01T12:00:00Z" + }, "Shape.algorithm names separated by commas, or all": { "description": "What the text in a box has to look like, in the sentence under it.", "hash": "sha256-bba80a8dec88", diff --git a/internal/gui/text/locale/registry/pl.json b/internal/gui/text/locale/registry/pl.json index c62b1a40..2dac251d 100644 --- a/internal/gui/text/locale/registry/pl.json +++ b/internal/gui/text/locale/registry/pl.json @@ -126,6 +126,7 @@ "Choice.format.log": { "hash": "sha256-836ff184e7b4", "other": "log" }, "Choice.format.md": { "hash": "sha256-21262a3cb533", "other": "md" }, "Choice.format.mp4": { "hash": "sha256-862c4ec62def", "other": "mp4" }, + "Choice.format.otlp": { "hash": "sha256-e81fa7c0883f", "other": "otlp" }, "Choice.format.pdf": { "hash": "sha256-c35b21d6ca39", "other": "pdf" }, "Choice.format.png": { "hash": "sha256-8f8cbb7dcf46", "other": "png" }, "Choice.format.pptx": { "hash": "sha256-1512e97a4b84", "other": "pptx" }, @@ -208,6 +209,8 @@ "Choice.rotate.180": { "hash": "sha256-7b69759630f8", "other": "180" }, "Choice.rotate.270": { "hash": "sha256-d8d1790737d5", "other": "270" }, "Choice.rotate.90": { "hash": "sha256-69f59c273b6e", "other": "90" }, + "Choice.signal.logs": { "hash": "sha256-98f38f12db22", "other": "logi" }, + "Choice.signal.traces": { "hash": "sha256-b5ac69fc467c", "other": "ślady" }, "Choice.status_mix.client-errors": { "hash": "sha256-cc47f4e83c84", "other": "błędy klienta" }, "Choice.status_mix.realistic": { "hash": "sha256-9d2f45fd706a", "other": "realistycznie" }, "Choice.status_mix.server-errors": { "hash": "sha256-af6f28d08a12", "other": "błędy serwera" }, @@ -313,6 +316,15 @@ "Detail.format/mp4.keyframe_interval": { "hash": "sha256-6c717248aaaa", "other": "Jak daleko od siebie są klatki, od których odtwarzacz może zacząć. Krótszy odstęp przyspiesza przewijanie długiego filmu i powiększa plik, bo każda z nich niesie cały obraz dwa razy." }, "Detail.format/mp4.quality": { "hash": "sha256-f6e1c3e1af83", "other": "Ile szczegółów zachowuje obraz. Wyższa wartość wygląda lepiej i zajmuje więcej pliku, zostawiając mniej miejsca na dopełnienie." }, "Detail.format/mp4.width": { "hash": "sha256-3085ac44f0e0", "other": "Jak szeroki jest obraz. Gdy pole zostaje puste, wybierany jest rozmiar, który mieści się w zamówionych bajtach." }, + "Detail.format/otlp.batch": { "hash": "sha256-3c2c865d96f5", "other": "Ile spanów albo rekordów logów ma każda linia. 512 to paczka, którą SDK OpenTelemetry wysyła domyślnie. Paczki ma tylko układ rekord na wiersz, a przy innym układzie paczka jest odrzucana." }, + "Detail.format/otlp.depth": { "hash": "sha256-d1caceabbb7a", "other": "Ile spanów ma każdy ślad, każdy jako dziecko poprzedniego. 1 zostawia każdy span bez rodzica. Kształtuje spany, a przy sygnale logi jest odrzucane." }, + "Detail.format/otlp.formatting": { "hash": "sha256-a0bda4ebdc96", "other": "Jak ułożony jest plik. Przy rekord na wiersz każda paczka stoi w osobnej linii, tak jak zapisuje ją eksporter plików OTLP i jak czyta ją odbiornik plików OpenTelemetry Collectora. Przy zminifikowany i z wcięciami cały plik jest jednym żądaniem, czyli treścią do wysłania na endpoint OTLP, i taki plik jest jednym dokumentem JSON niezależnie od końcówki nazwy. Wysłany jako treść żądania plik o kilku liniach traci w Collectorze każdą linię po pierwszej, a jego odbiornik plików nie czyta nic z pliku z wcięciami." }, + "Detail.format/otlp.level_mix": { "hash": "sha256-96679b162c65", "other": "Jakie poziomy ważności niosą rekordy logów. Realistycznie daje głównie INFO i trochę pozostałych. Kształtuje rekordy logów, a przy sygnale ślady jest odrzucane." }, + "Detail.format/otlp.rate": { "hash": "sha256-ce402f8f7753", "other": "Ile śladów albo rekordów logów zaczyna się w każdej sekundzie." }, + "Detail.format/otlp.signal": { "hash": "sha256-a0541e2d1349", "other": "Jaki rodzaj telemetrii jest w pliku. Przy ślady plik niesie spany, a przy logi rekordy logów. Plik niesie jeden rodzaj, tak jak wymaga tego format plików OTLP." }, + "Detail.format/otlp.start": { "hash": "sha256-a8823ab3b44e", "other": "Chwila, którą datowany jest pierwszy span albo rekord logu. Kolejne idą po niej w ustawionym tempie. Zmień ją, gdy system, który odbiera plik, odrzuca albo ukrywa zbyt stare dane." }, + "Detail.format/otlp.status_mix": { "hash": "sha256-06462e0e2639", "other": "Jakimi kodami odpowiedzi kończą się żądania. Kod 500 albo wyższy oznacza swój span jako błąd, tak jak wymagają konwencje HTTP OpenTelemetry, a każdy inny kod zostawia status nieustawiony. Kształtuje spany, a przy sygnale logi jest odrzucane." }, + "Detail.format/otlp.trace_context": { "hash": "sha256-634b48d43770", "other": "Czy każdy rekord logu podaje ślad i span, w którym go zapisano. Plik nie ma spanów, więc te identyfikatory nie pasują do niczego w nim. Kształtuje rekordy logów, a przy sygnale ślady jest odrzucane." }, "Detail.format/pdf.author": { "hash": "sha256-33b1ee363d86", "other": "Autor we właściwościach dokumentu. Gdy pole zostaje puste, dokument nie podaje żadnego." }, "Detail.format/pdf.created": { "hash": "sha256-76abcd78cc63", "other": "Kiedy według dokumentu powstał. Czas zapisany bez strefy zostaje bez niej, a none pomija datę." }, "Detail.format/pdf.creator": { "hash": "sha256-57ed4066a447", "other": "Program, w którym według dokumentu go napisano, na przykład Microsoft Word. Gdy pole zostaje puste, dokument nie podaje żadnego." }, @@ -409,6 +421,7 @@ "Format.log": { "hash": "sha256-b6ef69eb3499", "other": "Log serwera i aplikacji" }, "Format.md": { "hash": "sha256-0e52f6b9d025", "other": "Markdown" }, "Format.mp4": { "hash": "sha256-1f5265d4bfbb", "other": "Wideo MP4" }, + "Format.otlp": { "hash": "sha256-3aecb4b59e76", "other": "OpenTelemetry Protocol JSON" }, "Format.pdf": { "hash": "sha256-ccf9cd24dc07", "other": "Portable Document Format" }, "Format.png": { "hash": "sha256-016d63e71b45", "other": "Portable Network Graphics" }, "Format.pptx": { "hash": "sha256-33f651fb2245", "other": "PowerPoint (Office Open XML)" }, @@ -453,6 +466,7 @@ "Label.attachment_size": { "hash": "sha256-151d41e8a53c", "other": "Rozmiar załączników" }, "Label.attachments": { "hash": "sha256-634de1147750", "other": "Liczba załączników" }, "Label.author": { "hash": "sha256-d95082a2ee57", "other": "Autor" }, + "Label.batch": { "hash": "sha256-320b5d80f8cf", "other": "Paczka" }, "Label.bbox": { "hash": "sha256-708f44b1bd98", "other": "Ramka (bbox)" }, "Label.bit_depth": { "hash": "sha256-cc62f2e89a82", "other": "Głębia bitowa" }, "Label.body": { "hash": "sha256-6ccaa6415b5e", "other": "Treść" }, @@ -533,8 +547,10 @@ "Label.rows": { "hash": "sha256-101f2ff3de22", "other": "Wiersze" }, "Label.sample": { "hash": "sha256-95a2f1edb08a", "other": "Rozmiar próbki" }, "Label.sample_rate": { "hash": "sha256-218e0d43275e", "other": "Częstotliwość próbkowania" }, + "Label.signal": { "hash": "sha256-1e9806e4227b", "other": "Sygnał" }, "Label.slides": { "hash": "sha256-a0ffe3818444", "other": "Slajdy" }, "Label.spread": { "hash": "sha256-b11fb1f593a8", "other": "Rozrzut" }, + "Label.start": { "hash": "sha256-e4bb9f1ece9a", "other": "Początek" }, "Label.status_mix": { "hash": "sha256-e5e3006b5ecd", "other": "Kody odpowiedzi" }, "Label.structure": { "hash": "sha256-7203c2b3dad3", "other": "Struktura" }, "Label.styles": { "hash": "sha256-3626e7fdea1c", "other": "Style" }, @@ -542,6 +558,7 @@ "Label.text_encoding": { "hash": "sha256-f6726ca0b9fe", "other": "Kodowanie tekstu" }, "Label.timestamps": { "hash": "sha256-755f099dbf49", "other": "Znaczniki czasu" }, "Label.title": { "hash": "sha256-7e8cd2056da7", "other": "Tytuł" }, + "Label.trace_context": { "hash": "sha256-07b45d3adabb", "other": "Kontekst śladu" }, "Label.unlocated": { "hash": "sha256-9d9b671e4837", "other": "Bez położenia" }, "Label.unpacked_limit": { "hash": "sha256-ea378e1bd7aa", "other": "Limit po rozpakowaniu" }, "Label.vertices": { "hash": "sha256-f5af129ea336", "other": "Punkty" }, @@ -562,6 +579,7 @@ "Question.upload-validation": { "hash": "sha256-950469ca4b3a", "other": "Czy mój formularz przesyłania plików przyjmuje to, co powinien, i odrzuca resztę?" }, "Shape.a checksum in hexadecimal": { "hash": "sha256-62cb052ebc94", "other": "suma kontrolna zapisana szesnastkowo" }, "Shape.a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": { "hash": "sha256-a40bd1decdc9", "other": "data, np. 2024-02-29 albo 2024-02-29T13:45:00+02:00, albo none" }, + "Shape.a moment in UTC such as 2026-08-01T12:00:00Z": { "hash": "sha256-97f0219d13b4", "other": "chwila w UTC, np. 2026-08-01T12:00:00Z" }, "Shape.algorithm names separated by commas, or all": { "hash": "sha256-bba80a8dec88", "other": "nazwy algorytmów rozdzielone przecinkami albo all" }, "Shape.any text": { "hash": "sha256-06c83efcd22e", "other": "dowolny tekst" }, "Shape.extensions separated by commas": { "hash": "sha256-69f0be0e37e3", "other": "rozszerzenia rozdzielone przecinkami" }, diff --git a/internal/gui/text/locale/said/en.json b/internal/gui/text/locale/said/en.json index 18d71016..335ff10a 100644 --- a/internal/gui/text/locale/said/en.json +++ b/internal/gui/text/locale/said/en.json @@ -2091,7 +2091,7 @@ "other": "this size needs a padding chunk and the smallest one costs {{.ChunkHeader}} B, so nothing between {{.Fixed}} and {{.Fixed2}} B can be reached" }, "format.TwoSettings": { - "description": "Said by internal/format/logfile - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Of}}, {{.By}}, written the way the program writes them - keep each spelled exactly that way.", + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Of}}, {{.By}}, written the way the program writes them - keep each spelled exactly that way.", "hash": "sha256-a61ae8d11eb8", "other": "{{.Of}} and {{.By}}" }, @@ -2461,6 +2461,56 @@ "hash": "sha256-2d553380ec9d", "other": "a film of {{.Duration}} at {{.FPS}} frames a second with a key frame every {{.Interval}} and a new {{.Width}}x{{.Height}} picture every {{.Change}} takes {{.Bytes}} B, and the file always carries a free box, which costs {{.Free}} B even when it holds nothing" }, + "otlp.AFileHoldsWholeRecords": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run.", + "hash": "sha256-8bd4f63f9cb2", + "other": "a file holds whole records, and the longest one these settings can write needs that much" + }, + "otlp.AFileThisLargeEndsAfter": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Rate}}, written the way the program writes them - keep each spelled exactly that way.", + "hash": "sha256-511e89a36bd2", + "other": "a file of this size at a rate of {{.Rate}} would date its last records after the year 2262. Choose an earlier start, a higher rate or a smaller file" + }, + "otlp.ChooseAStartInThoseYears": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run.", + "hash": "sha256-c72b07e145c8", + "other": "Choose a start in those years, such as 2026-08-01T12:00:00Z." + }, + "otlp.DropOneOfTheTwo": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Why}}, written the way the program writes them - keep each spelled exactly that way.", + "hash": "sha256-9d99490328fe", + "other": "{{.Why}}. Drop one of the two, or change the other" + }, + "otlp.ItHasToLieInTheYears": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.First}}, {{.Last}}, written the way the program writes them - keep each spelled exactly that way.", + "hash": "sha256-2b01e1cc25a8", + "other": "it has to lie in the years {{.First}} to {{.Last}}" + }, + "otlp.ItShapesLogRecordsAndSignal": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Signal}}, written the way the program writes them - keep each spelled exactly that way.", + "hash": "sha256-bcb9bc6dcb7b", + "other": "it shapes log records, and signal {{.Signal}} writes none" + }, + "otlp.ItShapesSpansAndSignal": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Signal}}, written the way the program writes them - keep each spelled exactly that way.", + "hash": "sha256-0d5289829281", + "other": "it shapes spans, and signal {{.Signal}} writes none" + }, + "otlp.ItTakesAMomentInUTC": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run.", + "hash": "sha256-3cebaa0f99ae", + "other": "it takes a moment in UTC written as 2026-08-01T12:00:00Z" + }, + "otlp.OnlyOneLayoutHasBatches": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.Lines}}, {{.Layout}}, written the way the program writes them - keep each spelled exactly that way.", + "hash": "sha256-b25305f5d3ab", + "other": "only {{.Lines}} cuts the file into batches, and {{.Layout}} writes one request" + }, + "otlp.WriteTheMomentThatWay": { + "description": "Said by internal/format/otlp - a refusal under the box it is about or at the foot of the form, or a note after a run.", + "hash": "sha256-b21a2d65cac8", + "other": "Write the date, a T, the time to the second and a Z, with no fraction of a second." + }, "pdf.Carrying": { "description": "Said by internal/format/pdf - a refusal under the box it is about or at the foot of the form, or a note after a run. Carries {{.What}}, written the way the program writes them - keep each spelled exactly that way.", "hash": "sha256-83e9af0cb805", diff --git a/internal/gui/text/locale/said/pl.json b/internal/gui/text/locale/said/pl.json index b6f33d1f..e35ffeed 100644 --- a/internal/gui/text/locale/said/pl.json +++ b/internal/gui/text/locale/said/pl.json @@ -490,6 +490,16 @@ "manifest.Unreadable": { "hash": "sha256-bb90fafec850", "other": "{{.Path}} nie daje się odczytać jako manifest: {{.Detail}}" }, "manifest.ValueTooLarge": { "hash": "sha256-65f9e8304926", "other": "{{.Path}} zawiera pojedynczą wartość dłuższą niż {{.Limit}} B, zaczynającą się mniej więcej {{.At}} B od początku, a to narzędzie czyta z manifestu naraz najwyżej tyle. Wpis manifestu zapisanego przez to narzędzie zajmuje kilobajt lub dwa, więc sprawdź, czy to manifest zapisany przez przebieg" }, "mp4.MinimumReasonPictures": { "hash": "sha256-2d553380ec9d", "other": "film o długości {{.Duration}} przy {{.FPS}} klatkach na sekundę z klatką kluczową co {{.Interval}} i nowym obrazem {{.Width}}x{{.Height}} co {{.Change}} zajmuje {{.Bytes}} B, a plik zawsze niesie pudełko free, które kosztuje {{.Free}} B nawet puste" }, + "otlp.AFileHoldsWholeRecords": { "hash": "sha256-8bd4f63f9cb2", "other": "plik składa się z całych rekordów, a najdłuższy, jaki te ustawienia mogą zapisać, potrzebuje tyle" }, + "otlp.AFileThisLargeEndsAfter": { "hash": "sha256-511e89a36bd2", "other": "plik tej wielkości przy tempie {{.Rate}} datowałby ostatnie rekordy po roku 2262. Wybierz wcześniejszy początek, wyższe tempo albo mniejszy plik" }, + "otlp.ChooseAStartInThoseYears": { "hash": "sha256-c72b07e145c8", "other": "Wybierz początek w tych latach, na przykład 2026-08-01T12:00:00Z." }, + "otlp.DropOneOfTheTwo": { "hash": "sha256-9d99490328fe", "other": "{{.Why}}. Usuń jedno z dwóch albo zmień drugie" }, + "otlp.ItHasToLieInTheYears": { "hash": "sha256-2b01e1cc25a8", "other": "musi leżeć w latach od {{.First}} do {{.Last}}" }, + "otlp.ItShapesLogRecordsAndSignal": { "hash": "sha256-bcb9bc6dcb7b", "other": "kształtuje rekordy logów, a sygnał {{.Signal}} nie zapisuje żadnego" }, + "otlp.ItShapesSpansAndSignal": { "hash": "sha256-0d5289829281", "other": "kształtuje spany, a sygnał {{.Signal}} nie zapisuje żadnego" }, + "otlp.ItTakesAMomentInUTC": { "hash": "sha256-3cebaa0f99ae", "other": "przyjmuje chwilę w UTC zapisaną jako 2026-08-01T12:00:00Z" }, + "otlp.OnlyOneLayoutHasBatches": { "hash": "sha256-b25305f5d3ab", "other": "tylko {{.Lines}} dzieli plik na paczki, a {{.Layout}} zapisuje jedno żądanie" }, + "otlp.WriteTheMomentThatWay": { "hash": "sha256-b21a2d65cac8", "other": "Zapisz datę, literę T, czas co do sekundy i literę Z, bez ułamka sekundy." }, "pdf.Carrying": { "hash": "sha256-83e9af0cb805", "other": " z {{.What}}" }, "pdf.ChooseOneSize": { "hash": "sha256-61da6826cc02", "other": "wybierz jeden rozmiar, na przykład a4" }, "pdf.ChooseOneWay": { "hash": "sha256-5f59b0be4c6d", "other": "wybierz {{.Portrait}} albo {{.Landscape}}" }, diff --git a/internal/oracle/oracle.go b/internal/oracle/oracle.go index 8427deda..74380c89 100644 --- a/internal/oracle/oracle.go +++ b/internal/oracle/oracle.go @@ -357,6 +357,10 @@ var checkers = map[string]Checker{ // pandoc's RTF reader. See rtfscripts.go. "pandoc-rtf": pandocRTF, + + // The OpenTelemetry Collector, asked to write the file back. See + // otelscripts.go. + "otelcol": otelcol, } // DecodedFrames is how many frames the libaom oracle decoded, read from what @@ -488,148 +492,3 @@ func sevenZip() (string, bool) { } return "", false } - -// Strict runs the structural checker written against the specification. -// -// The tolerant readers above answer "would a real viewer accept this". This -// answers "is it actually well formed". Measured, the two are different -// questions: ffprobe ignores a wrong size in the RIFF header, Pillow does not -// verify the checksum of an ancillary PNG chunk, and a PDF reader rebuilds a -// cross reference table whose offset is off by one. -// -// It is written in another language, to the specification, so it is not our -// own code judging our own code. -// The optional settings are handed to the checker after the path, as -// key=value words. They exist for the formats whose file SHAPE is a setting - -// a CSV separated by semicolons is well formed and a checker told to split on -// commas would call it a single column table. The checker is told rather than -// left to work it out, because a checker that guesses the separator would -// happily agree with a file that used the wrong one, which is the question a -// guard has to answer instead. -func Strict(formatID, path string, settings ...string) Result { - python, ok := inPath("python")() - if !ok { - return Result{Available: false, Tool: "strict structural check"} - } - script, ok := strictScriptPath() - if !ok { - return Result{Available: false, Tool: "strict structural check"} - } - - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - defer cancel() - - //nolint:gosec // same as above - our own script, run against a file this - // tool wrote a moment ago - // nosemgrep: go.lang.security.audit.dangerous-exec-command.dangerous-exec-command - cmd := exec.CommandContext(ctx, python, append([]string{script, formatID, path}, settings...)...) - var out, errOut strings.Builder - cmd.Stdout = &out - cmd.Stderr = &errOut - _ = cmd.Run() - - text := strings.TrimSpace(out.String()) - res := Result{Available: true, Tool: "strict structural check", Output: text} - switch { - case strings.HasPrefix(text, "OK"): - case strings.HasPrefix(text, "FAIL"): - res.Err = fmt.Errorf("%s", strings.TrimSpace(strings.TrimPrefix(text, "FAIL"))) - default: - res.Err = fmt.Errorf("the checker said nothing useful: %s %s", text, strings.TrimSpace(errOut.String())) - } - return res -} - -// StrictKnows says whether the structural checker covers a format. -func StrictKnows(formatID string) bool { - switch formatID { - case "png", "wav", "pdf", "zip", "targz", "log", "csv", "json", "xml", "svg", "html", - // From its first day, because GDAL alone took files this one refuses - // (docs/GEOJSON-2026-10-07.md section 3). - "geojson", - // The same reason for mail: four readers took a message with no From - // line (docs/EML-2026-10-07.md section 5.1). - "eml", - "bmp", "gif", "ico", "jpg", "tiff", "webp", "avif", "jxl", "docx", "xlsx", "pptx", - // The two text formats joined on 2026-09-07, when they gained an - // encoding. Before that there was nothing here to check against - // beyond "these are the bytes we meant" - a claim about UTF-16 is a - // claim somebody else's decoder can settle. - "txt", "md", - // The two configuration formats, from the day they arrived. This layer - // carries more of them than usual: measured 2026-09-22, PyYAML takes a - // duplicate key that every other implementation refuses, and neither a - // parser nor a hash would report a field that moved. - "yaml", "toml", - // The first film, from the day it arrived (2026-10-06). The decoder - // says whether the frames that are there play, and only the container - // says whether they are all there. - "webm", - // The second, from the day it arrived (2026-10-07). The sample tables - // say how long every sample is, and only reading them against the - // media data says the tables and the samples agree. - // - // And rtf, from its first day, for the reason mail gave: five readers - // took eleven of twelve broken documents (docs/RTF-2026-10-15.md - // section 6.1 f), and none of them says whether a fallback agrees - // with the count before it. - "mp4", "rtf": - return true - } - return false -} - -// strictScriptPath finds the checker next to this source file, so the tests -// work wherever the repository is checked out. -// strictScriptPath finds the checker script, and has to look twice. -// -// The first way works on the machine that compiled the binary: the script sits -// beside this file, and runtime.Caller says where that is. -// -// The second way exists because that path is COMPILED IN, so a binary built -// here and carried anywhere else looks for a directory that is not there. -// Measured on 2026-09-09: the guard suite cross compiled on Windows and run in -// a Linux container reported "0 file(s) decoded strictly by Python" with python -// sitting in that container's PATH the whole time. Every structural check had -// been quietly unavailable since the container runs began, and it took two -// guards that refuse to pass on nothing to make it visible - an unavailable -// checker is a skip, and a skip reads like a check that ran. -// -// So the second way asks the working directory instead and walks up looking for -// the script at its place in the tree. A test binary runs from inside the tree -// it tests, which is exactly the case the compiled in path cannot cover. -func strictScriptPath() (string, bool) { - if _, thisFile, _, ok := runtime.Caller(0); ok { - p := filepath.Join(filepath.Dir(thisFile), "strict.py") - if _, err := os.Stat(p); err == nil { - return p, true - } - } - dir, err := os.Getwd() - if err != nil { - return "", false - } - return StrictScriptUnder(dir) -} - -// StrictScriptUnder walks up from dir looking for the checker script. -// -// Exported for the guard that holds it. The compiled in path above is chosen -// first on any machine that built the binary, so a guard calling -// strictScriptPath would prove that one and never reach this. -func StrictScriptUnder(dir string) (string, bool) { - for { - p := filepath.Join(dir, "internal", "oracle", "strict.py") - if _, err := os.Stat(p); err == nil { - return p, true - } - up := filepath.Dir(dir) - if up == dir { - // The top of the disk. Whatever sits above this belongs to - // something else, so the answer is no rather than the first - // checker that happens to turn up outside the tree. - return "", false - } - dir = up - } -} diff --git a/internal/oracle/otelscripts.go b/internal/oracle/otelscripts.go new file mode 100644 index 00000000..d2a45d82 --- /dev/null +++ b/internal/oracle/otelscripts.go @@ -0,0 +1,161 @@ +package oracle + +// otelcol is the OpenTelemetry Collector: its file receiver reads the file and +// its file exporter writes back what it understood. +// +// Reading alone would be a weak witness, measured 2026-11-10 +// (docs/OTLP-2026-11-10.md section 3): the Collector takes an enum written as +// a name and keys in snake case, both of which the specification forbids, and +// it drops a line it cannot read without a word in its log. So the question +// asked here is the stronger one - does what comes back equal what went in, +// byte for byte and batch for batch. A file written the way the Collector +// writes one passes, a line it dropped is missing from the answer, and a +// field it read differently comes back spelled differently. +// +// The Collector is named through TFG_OTELCOL or found on the path as +// otelcol-contrib, the distribution that carries both components. It is a +// server rather than a command, so the script starts it on a directory of its +// own, waits for the batches and stops it. No port is opened, which is what +// lets several of these run side by side. +var otelcol = Checker{ + Name: "the OpenTelemetry Collector", + find: inPath("python"), + args: func(p string) []string { return []string{"-c", otelcolScript, p} }, + accept: expectOK("the OpenTelemetry Collector"), +} + +const otelcolScript = ` +import json, os, shutil, subprocess, sys, tempfile, time + +# Counted from here rather than from the moment the Collector starts: whoever +# runs this script stops it after a minute, and stops this process alone. A +# Collector still running then would be left behind, so everything below ends +# well inside that minute (review of #230). +STARTED = time.time() +BUDGET = 40 +LINE_CEILING = 1 << 20 +SIGNALS = {b'{"resourceSpans"': "traces", b'{"resourceLogs"': "logs", b'{"resourceMetrics"': "metrics"} + +named = os.environ.get("TFG_OTELCOL", "") +collector = named or shutil.which("otelcol-contrib") +if not collector: + print("SKIP otelcol-contrib is not installed") + sys.exit(0) +if not os.path.isfile(collector): + print("FAIL TFG_OTELCOL names %s and no such file is there" % collector) + sys.exit(1) + +def squeezed(data): + # The document with the whitespace between its tokens taken out and every + # token left as it is written. Parsing and writing again would hide a + # number the generator spelled differently from the Collector. + out, in_text, escaped = bytearray(), False, False + for b in data: + if in_text: + out.append(b) + if escaped: + escaped = False + elif b == 0x5C: + escaped = True + elif b == 0x22: + in_text = False + elif b not in b" \t\r\n": + out.append(b) + in_text = b == 0x22 + return bytes(out) + +def is_object(line): + try: + return isinstance(json.loads(line), dict) + except (ValueError, RecursionError): + return False + +with open(sys.argv[1], "rb") as f: + data = f.read() +pieces = data.split(b"\n") +by_line = data.endswith(b"\n") and all(is_object(p) for p in pieces[:-1]) +if by_line: + wanted = pieces[:-1] +else: + # One request. The file receiver reads lines, so it is handed the request + # on one line, and told to read a line that long. + try: + json.loads(data) + except RecursionError: + print("FAIL the file is nested deeper than a JSON reader goes") + sys.exit(1) + except ValueError as err: + print("FAIL the file is neither a batch on every line nor one JSON document: %s" % err) + sys.exit(1) + wanted = [squeezed(data)] + +signal = next((name for start, name in SIGNALS.items() if wanted[0].startswith(start)), None) +if signal is None: + print("FAIL the file does not open with resourceSpans, resourceLogs or resourceMetrics") + sys.exit(1) + +work = tempfile.mkdtemp(prefix="tfg-otelcol-") +try: + os.mkdir(os.path.join(work, "in")) + with open(os.path.join(work, "in", "data.jsonl"), "wb") as f: + f.write(b"\n".join(wanted) + b"\n") + out = os.path.join(work, "out.jsonl") + longest = max(len(w) for w in wanted) + lines = ["receivers:", " otlp_json_file:", " include: ['%s']" % (work.replace(os.sep, "/") + "/in/*.jsonl"), + " start_at: beginning", " include_file_name: false"] + if not by_line and longest > LINE_CEILING: + lines.append(" max_log_size: %d" % (longest + 1024)) + lines += ["exporters:", " file:", " path: '%s'" % out.replace(os.sep, "/"), " flush_interval: 100ms", + "service:", " telemetry:", " metrics:", " level: none", + " pipelines:", " %s:" % signal, " receivers: [otlp_json_file]", " exporters: [file]"] + config = os.path.join(work, "config.yaml") + with open(config, "w", encoding="utf-8", newline="\n") as f: + f.write("\n".join(lines) + "\n") + + said = open(os.path.join(work, "collector.log"), "wb") + process = subprocess.Popen([collector, "--config", config], stdout=said, stderr=said) + def written(): + # Whole lines only. The exporter writes through a buffer, so the last + # line of the file can be half of a batch for a moment. + if not os.path.exists(out): + return [] + with open(out, "rb") as f: + return f.read().split(b"\n")[:-1] + + got, deadline = [], STARTED + BUDGET + try: + while time.time() < deadline and process.poll() is None: + time.sleep(0.2) + got = written() + if len(got) >= len(wanted): + # One more look, so a batch the Collector wrote twice shows. + time.sleep(0.6) + got = written() + break + finally: + # Asked before it is stopped: once killed, every Collector has ended + # with a code, and a file it dropped would read as one that never ran. + ended = process.poll() is not None + process.kill() + process.wait() + said.close() + + if ended and not got: + with open(os.path.join(work, "collector.log"), "rb") as f: + tail = f.read()[-600:].decode("utf-8", "replace") + print("FAIL the Collector did not run: %s" % tail.strip()) + sys.exit(1) + if len(got) != len(wanted): + print("FAIL %d of %d batches came back - the Collector dropped the rest without a word, which is what it does " + "with a line longer than %d bytes and with one it cannot read" % (len(got), len(wanted), LINE_CEILING)) + sys.exit(1) + for number, (a, b) in enumerate(zip(wanted, got), start=1): + if a != b: + at = next((i for i in range(min(len(a), len(b))) if a[i] != b[i]), min(len(a), len(b))) + print("FAIL batch %d came back different at byte %d: wrote ...%s and the Collector writes ...%s" % ( + number, at, a[max(0, at - 40):at + 40].decode("utf-8", "replace"), b[max(0, at - 40):at + 40].decode("utf-8", "replace"))) + sys.exit(1) + print("OK %d batches of %s came back byte for byte, the longest %d bytes" % (len(wanted), signal, longest)) +finally: + shutil.rmtree(work, ignore_errors=True) +` diff --git a/internal/oracle/strict.go b/internal/oracle/strict.go new file mode 100644 index 00000000..9525d770 --- /dev/null +++ b/internal/oracle/strict.go @@ -0,0 +1,166 @@ +package oracle + +// The structural checker: the layer written to the specification rather than +// borrowed from somebody's reader. It stood at the end of oracle.go until +// 2026-11-11, when the list of formats it covers took that file past the +// length a person can follow. + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "time" +) + +// Strict runs the structural checker written against the specification. +// +// The tolerant readers above answer "would a real viewer accept this". This +// answers "is it actually well formed". Measured, the two are different +// questions: ffprobe ignores a wrong size in the RIFF header, Pillow does not +// verify the checksum of an ancillary PNG chunk, and a PDF reader rebuilds a +// cross reference table whose offset is off by one. +// +// It is written in another language, to the specification, so it is not our +// own code judging our own code. +// The optional settings are handed to the checker after the path, as +// key=value words. They exist for the formats whose file SHAPE is a setting - +// a CSV separated by semicolons is well formed and a checker told to split on +// commas would call it a single column table. The checker is told rather than +// left to work it out, because a checker that guesses the separator would +// happily agree with a file that used the wrong one, which is the question a +// guard has to answer instead. +func Strict(formatID, path string, settings ...string) Result { + python, ok := inPath("python")() + if !ok { + return Result{Available: false, Tool: "strict structural check"} + } + script, ok := strictScriptPath() + if !ok { + return Result{Available: false, Tool: "strict structural check"} + } + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + //nolint:gosec // same as above - our own script, run against a file this + // tool wrote a moment ago + // nosemgrep: go.lang.security.audit.dangerous-exec-command.dangerous-exec-command + cmd := exec.CommandContext(ctx, python, append([]string{script, formatID, path}, settings...)...) + var out, errOut strings.Builder + cmd.Stdout = &out + cmd.Stderr = &errOut + _ = cmd.Run() + + text := strings.TrimSpace(out.String()) + res := Result{Available: true, Tool: "strict structural check", Output: text} + switch { + case strings.HasPrefix(text, "OK"): + case strings.HasPrefix(text, "FAIL"): + res.Err = fmt.Errorf("%s", strings.TrimSpace(strings.TrimPrefix(text, "FAIL"))) + default: + res.Err = fmt.Errorf("the checker said nothing useful: %s %s", text, strings.TrimSpace(errOut.String())) + } + return res +} + +// StrictKnows says whether the structural checker covers a format. +func StrictKnows(formatID string) bool { + switch formatID { + case "png", "wav", "pdf", "zip", "targz", "log", "csv", "json", "xml", "svg", "html", + // From its first day, because GDAL alone took files this one refuses + // (docs/GEOJSON-2026-10-07.md section 3). + "geojson", + // From its first day as well: the Collector takes an enum written as a + // name, keys in snake case and a span that ends before it starts + // (docs/OTLP-2026-11-10.md section 3). + "otlp", + // The same reason for mail: four readers took a message with no From + // line (docs/EML-2026-10-07.md section 5.1). + "eml", + "bmp", "gif", "ico", "jpg", "tiff", "webp", "avif", "jxl", "docx", "xlsx", "pptx", + // The two text formats joined on 2026-09-07, when they gained an + // encoding. Before that there was nothing here to check against + // beyond "these are the bytes we meant" - a claim about UTF-16 is a + // claim somebody else's decoder can settle. + "txt", "md", + // The two configuration formats, from the day they arrived. This layer + // carries more of them than usual: measured 2026-09-22, PyYAML takes a + // duplicate key that every other implementation refuses, and neither a + // parser nor a hash would report a field that moved. + "yaml", "toml", + // The first film, from the day it arrived (2026-10-06). The decoder + // says whether the frames that are there play, and only the container + // says whether they are all there. + "webm", + // The second, from the day it arrived (2026-10-07). The sample tables + // say how long every sample is, and only reading them against the + // media data says the tables and the samples agree. + // + // And rtf, from its first day, for the reason mail gave: five readers + // took eleven of twelve broken documents (docs/RTF-2026-10-15.md + // section 6.1 f), and none of them says whether a fallback agrees + // with the count before it. + "mp4", "rtf": + return true + } + return false +} + +// strictScriptPath finds the checker next to this source file, so the tests +// work wherever the repository is checked out. +// strictScriptPath finds the checker script, and has to look twice. +// +// The first way works on the machine that compiled the binary: the script sits +// beside this file, and runtime.Caller says where that is. +// +// The second way exists because that path is COMPILED IN, so a binary built +// here and carried anywhere else looks for a directory that is not there. +// Measured on 2026-09-09: the guard suite cross compiled on Windows and run in +// a Linux container reported "0 file(s) decoded strictly by Python" with python +// sitting in that container's PATH the whole time. Every structural check had +// been quietly unavailable since the container runs began, and it took two +// guards that refuse to pass on nothing to make it visible - an unavailable +// checker is a skip, and a skip reads like a check that ran. +// +// So the second way asks the working directory instead and walks up looking for +// the script at its place in the tree. A test binary runs from inside the tree +// it tests, which is exactly the case the compiled in path cannot cover. +func strictScriptPath() (string, bool) { + if _, thisFile, _, ok := runtime.Caller(0); ok { + p := filepath.Join(filepath.Dir(thisFile), "strict.py") + if _, err := os.Stat(p); err == nil { + return p, true + } + } + dir, err := os.Getwd() + if err != nil { + return "", false + } + return StrictScriptUnder(dir) +} + +// StrictScriptUnder walks up from dir looking for the checker script. +// +// Exported for the guard that holds it. The compiled in path above is chosen +// first on any machine that built the binary, so a guard calling +// strictScriptPath would prove that one and never reach this. +func StrictScriptUnder(dir string) (string, bool) { + for { + p := filepath.Join(dir, "internal", "oracle", "strict.py") + if _, err := os.Stat(p); err == nil { + return p, true + } + up := filepath.Dir(dir) + if up == dir { + // The top of the disk. Whatever sits above this belongs to + // something else, so the answer is no rather than the first + // checker that happens to turn up outside the tree. + return "", false + } + dir = up + } +} diff --git a/internal/oracle/strict.py b/internal/oracle/strict.py index 1c15bd25..12365328 100644 --- a/internal/oracle/strict.py +++ b/internal/oracle/strict.py @@ -4164,6 +4164,322 @@ def rtf_every(text, settings): fail(f"{piece!r} comes after the first paragraph, and RTF puts it in front of the text") +OTLP_LINE_CEILING = 1 << 20 +OTLP_SCHEMA = "https://opentelemetry.io/schemas/1.45.0" +OTLP_PADDING = "tfg.padding" +OTLP_NAMES = {"traces": ("resourceSpans", "scopeSpans", "spans"), "logs": ("resourceLogs", "scopeLogs", "logRecords")} +OTLP_STATUSES = {"realistic": {200, 201, 204, 301, 302, 304, 400, 401, 403, 404, 409, 429, 500, 502, 503}, + "success": {200, 201, 204, 206}, + "client-errors": {400, 401, 403, 404, 409, 410, 422, 429}, + "server-errors": {500, 502, 503, 504}} +# The severity numbers logs.proto gives the first of each range. +OTLP_LEVELS = {"DEBUG": 5, "INFO": 9, "WARN": 13, "ERROR": 17} +OTLP_LEVEL_MIXES = {"realistic": {"INFO", "WARN", "ERROR", "DEBUG"}, "quiet": {"INFO"}, + "errors": {"ERROR", "WARN", "INFO"}, "debug": {"DEBUG", "INFO", "WARN"}} +# What the HTTP conventions ask of a server span, in the order this tool writes them. +OTLP_REQUEST_ATTRIBUTES = ["http.request.method", "http.route", "url.path", "url.scheme", "http.response.status_code", + "server.address", "client.address", "user_agent.original"] +OTLP_HEX = re.compile(r"^[0-9a-f]+$") +# Nineteen digits: every moment from 2001 to 2262 counted in nanoseconds. +OTLP_TIME = re.compile(r"^[1-9][0-9]{18}$") + + +def otlp_dump(value, indented): + """The text one layout makes of a value: no whitespace at all, or two + spaces a level with every value on its own line.""" + import json + + if indented: + return json.dumps(value, indent=2, ensure_ascii=False) + return json.dumps(value, separators=(",", ":"), ensure_ascii=False) + + +def otlp_parted(text, expected): + """Where a text parts from the text it should be, with what stands there.""" + at = next((i for i, (a, b) in enumerate(zip(text, expected)) if a != b), min(len(text), len(expected))) + return f"at byte {at}, {text[max(0, at - 30):at + 30]!r}" + + +def otlp_not_json(literal): + """NaN, Infinity and -Infinity, which CPython's json reads and writes back + and no JSON grammar has. Read quietly, one of them in a value nothing else + looks at would pass the comparison of the layout (review of #230).""" + raise ValueError(f"{literal} is not a JSON number") + + +def otlp_object(text, where): + import json + + try: + doc = json.loads(text, parse_constant=otlp_not_json) + except RecursionError: + fail(f"{where} is nested deeper than a JSON reader goes") + except ValueError as exc: + fail(f"{where} is not JSON: {exc}") + if not isinstance(doc, dict): + fail(f"{where} is not a JSON object") + return doc + + +def otlp_batches(text, settings): + """The batches of the file, each read from text laid out the way formatting + says and written exactly the way this tool writes a batch.""" + layout = settings["formatting"] + if layout == "record-per-line": + if not text.endswith("\n"): + fail("the file does not end with a newline") + batches = [] + for number, line in enumerate(text[:-1].split("\n"), start=1): + size = len(line.encode("utf-8")) + if size > OTLP_LINE_CEILING: + fail(f"line {number} is {size} B, and the file receiver of the Collector drops a line longer than {OTLP_LINE_CEILING} B") + doc = otlp_object(line, f"line {number}") + if otlp_dump(doc, False) != line: + fail(f"line {number} is not written the way a batch is: it parts from the rule {otlp_parted(line, otlp_dump(doc, False))}") + batches.append(doc) + return batches + if layout not in ("minified", "indented"): + fail(f"the otlp check was told formatting={layout!r}, which is not a layout this tool writes") + doc = otlp_object(text, "the file") + expected = otlp_dump(doc, True) + "\n" if layout == "indented" else otlp_dump(doc, False) + if text != expected: + fail(f"the file is not laid out {layout}: it parts from the rule {otlp_parted(text, expected)}") + return [doc] + + +def otlp_records(batches, settings): + """Every record of the file in order, out of batches that each name one + resource and one scope and hold as many records as batch says.""" + top, mid, leaf = OTLP_NAMES[settings["signal"]] + records, sizes = [], [] + for number, doc in enumerate(batches, start=1): + if list(doc) != [top] or len(doc[top]) != 1: + fail(f"batch {number} is not one {top} entry: its members are {list(doc)}") + res = doc[top][0] + if list(res) != ["resource", mid, "schemaUrl"] or res["schemaUrl"] != OTLP_SCHEMA: + fail(f"batch {number}: the resource entry has the members {list(res)}, not a resource, {mid} and the schema of the conventions") + if list(res["resource"]) != ["attributes"] or [a["key"] for a in res["resource"]["attributes"]] != ["service.name", "service.version"]: + fail(f"batch {number}: the resource does not carry service.name and service.version and nothing else") + for a in res["resource"]["attributes"]: + if list(a) != ["key", "value"] or not otlp_text(f"the resource of batch {number}", a["value"]): + fail(f"batch {number}: the resource attribute {a['key']} is not one key with a text in it") + if len(res[mid]) != 1: + fail(f"batch {number} holds {len(res[mid])} scopes, not one") + scope = res[mid][0] + if list(scope) != ["scope", leaf, "schemaUrl"] or scope["schemaUrl"] != OTLP_SCHEMA or list(scope["scope"]) != ["name", "version"]: + fail(f"batch {number}: the scope entry has the members {list(scope)}, not a named scope, {leaf} and the schema of the conventions") + if not all(isinstance(v, str) and v for v in scope["scope"].values()): + fail(f"batch {number}: the scope does not carry its name and its version as text") + if not scope[leaf]: + fail(f"batch {number} holds no record, an envelope the specification asks senders not to write") + sizes.append(len(scope[leaf])) + records.extend(scope[leaf]) + if settings["formatting"] == "record-per-line": + size = settings["batch"] + if any(n != size for n in sizes[:-1]) or sizes[-1] > size: + fail(f"the batches hold {sizes[:6]} records, and batch={size} asks for that many on every line and no more on the last") + return records + + +def otlp_attributes(what, record, last): + """The attributes of a record by key, in the order written. The padding + attribute is taken out: it belongs on the last record of the file and on + no other, after every attribute that says something.""" + found = {} + for a in record["attributes"]: + if list(a) != ["key", "value"] or len(a["value"]) != 1 or a["key"] in found: + fail(f"{what} has an attribute that is not one key with one value, or a key twice") + found[a["key"]] = a["value"] + if (OTLP_PADDING in found) != last: + fail(f"{what} {'carries' if OTLP_PADDING in found else 'carries no'} {OTLP_PADDING} attribute, and the last record of the file is the one that does") + if last: + if list(found)[-1] != OTLP_PADDING: + fail(f"{what} does not carry {OTLP_PADDING} as its last attribute") + otlp_text(what, found.pop(OTLP_PADDING)) + return found + + +def otlp_text(what, value): + if list(value) != ["stringValue"] or not isinstance(value["stringValue"], str): + fail(f"{what} has a value that is not text where this tool writes text") + return value["stringValue"] + + +def otlp_whole(what, value): + """A whole number, which the JSON encoding of OTLP writes as text.""" + if list(value) != ["intValue"] or not isinstance(value["intValue"], str) or not value["intValue"].isdigit(): + fail(f"{what} has a whole number that is not written as text of digits") + return int(value["intValue"]) + + +def otlp_id(what, value, digits): + if not isinstance(value, str) or len(value) != digits or not OTLP_HEX.match(value) or not value.strip("0"): + fail(f"{what} is {value!r}, not {digits} hex digits in lower case with a bit set") + + +def otlp_time(what, value): + if not isinstance(value, str) or not OTLP_TIME.match(value): + fail(f"{what} is {value!r}, not a time in nanoseconds written as text of nineteen digits") + return int(value) + + +def otlp_spans(spans, settings, start, step): + """Spans in traces of depth spans each: a request, then the work inside it, + every span the child of the one before and lying inside it.""" + depth, traces, previous = settings["depth"], 0, None + for number, sp in enumerate(spans, start=1): + level, what = (number - 1) % depth, f"span {number}" + want = (["traceId", "spanId"] + (["parentSpanId"] if level else []) + + ["name", "kind", "startTimeUnixNano", "endTimeUnixNano", "attributes", "status"]) + if list(sp) != want: + fail(f"{what} has the members {list(sp)}, not {want}") + otlp_id(f"the trace id of {what}", sp["traceId"], 32) + otlp_id(f"the id of {what}", sp["spanId"], 16) + begin, end = otlp_time(f"the start of {what}", sp["startTimeUnixNano"]), otlp_time(f"the end of {what}", sp["endTimeUnixNano"]) + if end <= begin: + fail(f"{what} ends at or before the moment it starts") + attrs = otlp_attributes(what, sp, number == len(spans)) + if level == 0: + if begin != start + traces * step: + fail(f"{what} opens trace {traces + 1} at {begin}, and start and rate put it at {start + traces * step}") + traces += 1 + otlp_request(what, sp, attrs, settings) + else: + if sp["traceId"] != previous[0]["traceId"] or sp["parentSpanId"] != previous[0]["spanId"]: + fail(f"{what} is not the child of the span before it, in the same trace") + if type(sp["kind"]) is not int or sp["kind"] != 1 or sp["status"] != {}: + fail(f"{what} is work inside a request, and is not an internal span with its status unset") + if list(attrs) != ["code.function.name"] or otlp_text(what, attrs["code.function.name"]) != sp["name"]: + fail(f"{what} is not named after the function it carries") + if not (previous[1] < begin and end < previous[2]): + fail(f"{what} does not lie inside its parent") + previous = (sp, begin, end) + return f"{len(spans)} spans in {traces} traces" + + +def otlp_request(what, sp, attrs, settings): + """A server span of the HTTP conventions: named after its method and its + route, and an error exactly when the response code is 500 or above.""" + if list(attrs)[:len(OTLP_REQUEST_ATTRIBUTES)] != OTLP_REQUEST_ATTRIBUTES: + fail(f"{what} carries the attributes {list(attrs)}, not those the HTTP conventions ask of a server span") + code = otlp_whole(what, attrs["http.response.status_code"]) + failed = code >= 500 + if list(attrs)[len(OTLP_REQUEST_ATTRIBUTES):] != (["error.type"] if failed else []): + fail(f"{what} ends with {code} and {'carries no' if failed else 'carries an'} error.type beside it") + if type(sp["kind"]) is not int or sp["kind"] != 2: + fail(f"{what} is a request and its kind is {sp['kind']!r}, not 2 for a server span") + if sp["name"] != otlp_text(what, attrs["http.request.method"]) + " " + otlp_text(what, attrs["http.route"]): + fail(f"{what} is named {sp['name']!r}, not after its method and its route") + if code not in OTLP_STATUSES[settings["status_mix"]]: + fail(f"{what} ends with {code}, which status_mix={settings['status_mix']} does not hold") + if failed and (otlp_text(what, attrs["error.type"]) != str(code) or sp["status"] != {"code": 2}): + fail(f"{what} ends with {code} and is not recorded as an error with that code as its type") + if not failed and sp["status"] != {}: + fail(f"{what} ends with {code} and its status is {sp['status']!r}, where the conventions leave it unset") + + +def otlp_log_records(entries, settings, start, step): + """Log records one tick of the clock apart, each with a severity of the mix + and, when trace_context says so, the trace and the span it was written in.""" + context = ["flags", "traceId", "spanId"] if settings["trace_context"] else [] + want = ["timeUnixNano", "observedTimeUnixNano", "severityNumber", "severityText", "body", "attributes"] + context + for number, en in enumerate(entries, start=1): + what = f"log record {number}" + if list(en) != want: + fail(f"{what} has the members {list(en)}, not {want}") + at = otlp_time(f"the time of {what}", en["timeUnixNano"]) + if at != start + (number - 1) * step or en["observedTimeUnixNano"] != en["timeUnixNano"]: + fail(f"{what} is dated {at}, and start and rate put it at {start + (number - 1) * step} with the same moment observed") + level = en["severityText"] + if level not in OTLP_LEVEL_MIXES[settings["level_mix"]]: + fail(f"{what} has the severity {level!r}, which level_mix={settings['level_mix']} does not hold") + if type(en["severityNumber"]) is not int or en["severityNumber"] != OTLP_LEVELS[level]: + fail(f"{what} says {level} and carries the number {en['severityNumber']!r}, not {OTLP_LEVELS[level]}") + if not otlp_text(what, en["body"]): + fail(f"{what} says nothing") + attrs = otlp_attributes(what, en, number == len(entries)) + if list(attrs) != ["code.function.name", "code.line.number"]: + fail(f"{what} carries the attributes {list(attrs)}, not the function and the line it was written at") + otlp_text(what, attrs["code.function.name"]) + otlp_whole(what, attrs["code.line.number"]) + if context: + if type(en["flags"]) is not int or en["flags"] != 1: + fail(f"{what} names a trace and its flags are {en['flags']!r}, not 1") + otlp_id(f"the trace id of {what}", en["traceId"], 32) + otlp_id(f"the span id of {what}", en["spanId"], 16) + return f"{len(entries)} log records" + + +def check_otlp(data, settings=None): + """OTLP in its JSON encoding, made the way it was ordered. + + The Collector reads the same file in the reference tool beside this and is + asked to write it back byte for byte. What only this layer asks, because + it is told the settings: that the file is laid out as formatting says and + no line is longer than the file receiver reads, that every batch but the + last holds batch records, that the clock runs from start at rate, that the + traces are depth spans deep with every child inside its parent, that a + span is an error exactly when its response code says so, that the + severities and the response codes come from the mix ordered, and that the + padding attribute sits on the last record and on no other. + + Measured 2026-11-10 (docs/OTLP-2026-11-10.md section 3): the Collector + reads an enum written as a name, keys in snake case and a span that ends + before it starts, and drops a line it cannot read without a word. All of + them are refused here. + """ + import calendar + import time + + given = settings or {} + settings = { + "signal": given.get("signal", "traces"), + "formatting": given.get("formatting", "record-per-line"), + "start": given.get("start", "2026-08-01T12:00:00Z"), + "status_mix": given.get("status_mix", "realistic"), + "level_mix": given.get("level_mix", "realistic"), + "trace_context": given.get("trace_context", "true") == "true", + } + # The numbers are read against the ranges the format declares, so a word + # or a nought handed to this check is a refusal that names the setting + # rather than a traceback a guard would read as the checker falling over + # (review of #230). + for name, default, most in (("batch", "512", 1000), ("rate", "1", 1000000), ("depth", "3", 100)): + told = given.get(name, default) + if not told.isdigit() or not 1 <= int(told) <= most: + fail(f"the otlp check was told {name}={told!r}, and {name} is a whole number from 1 to {most}") + settings[name] = int(told) + if settings["signal"] not in OTLP_NAMES: + fail(f"the otlp check was told signal={settings['signal']!r}, which is not a kind of telemetry this tool writes") + if settings["status_mix"] not in OTLP_STATUSES or settings["level_mix"] not in OTLP_LEVEL_MIXES: + fail(f"the otlp check was told status_mix={settings['status_mix']!r} and level_mix={settings['level_mix']!r}, and one of them is not a mix this tool draws from") + if data.startswith(b"\xef\xbb\xbf"): + fail("the file starts with a byte order mark") + try: + text = data.decode("utf-8") + except UnicodeDecodeError as exc: + fail(f"not valid UTF-8: {exc}") + try: + start = calendar.timegm(time.strptime(settings["start"], "%Y-%m-%dT%H:%M:%SZ")) * 10**9 + except ValueError: + fail(f"the otlp check was told start={settings['start']!r}, which is not a moment written as 2026-08-01T12:00:00Z") + batches = otlp_batches(text, settings) + step = 10**9 // settings["rate"] + # The checks name the shapes this tool could get wrong. A shape nobody + # thought of still has to come out as a refusal with a reason rather than + # a Python traceback, because a crash also turns a guard red and proves + # nothing (the lesson of check_geojson, review of #171). + try: + records = otlp_records(batches, settings) + if settings["signal"] == "traces": + held = otlp_spans(records, settings, start, step) + else: + held = otlp_log_records(records, settings, start, step) + except (TypeError, KeyError, IndexError, AttributeError, ValueError) as exc: + fail(f"a record is not shaped the way this tool writes one: {type(exc).__name__}: {exc}") + ok(f"{held}, {len(batches)} batches, laid out {settings['formatting']}") + + CHECKS = {"png": check_png, "wav": check_wav, "pdf": check_pdf, "zip": check_zip, "log": check_log, "csv": check_csv, "json": check_json, "xml": check_xml, "svg": check_svg, "html": check_html, "targz": check_targz, @@ -4173,12 +4489,12 @@ def rtf_every(text, settings): "docx": check_docx, "xlsx": check_xlsx, "pptx": check_pptx, "txt": check_txt, "md": check_md, "yaml": check_yaml, "toml": check_toml, "geojson": check_geojson, - "eml": check_eml, "rtf": check_rtf} + "eml": check_eml, "rtf": check_rtf, "otlp": check_otlp} # Checks that take the shape of the file as well as its bytes. Everything else # is handed the bytes alone, so adding a setting to one check cannot change how # any other one is called. -TAKES_SETTINGS = {"csv", "txt", "md", "json", "xml", "html", "geojson", "eml", "rtf"} +TAKES_SETTINGS = {"csv", "txt", "md", "json", "xml", "html", "geojson", "eml", "rtf", "otlp"} def run_check(kind, body, settings): """Runs one check. It ends by raising its Verdict, or by an exception.""" diff --git a/web/assets/social-preview.png b/web/assets/social-preview.png index d0ec5700..85d72cc6 100644 Binary files a/web/assets/social-preview.png and b/web/assets/social-preview.png differ diff --git a/web/content/ar/site.json b/web/content/ar/site.json index 4650cf8d..65acd0bd 100644 --- a/web/content/ar/site.json +++ b/web/content/ar/site.json @@ -276,7 +276,8 @@ "the id of a picture format, as tfg formats lists them": "معرّف صيغة صورة، كما يسردها tfg formats", "the password, in plain text": "كلمة المرور، كنص عادي", "any text": "أي نص", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "تاريخ مثل 2024-02-29 أو 2024-02-29T13:45:00+02:00، أو none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "تاريخ مثل 2024-02-29 أو 2024-02-29T13:45:00+02:00، أو none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "لحظة بالتوقيت العالمي المنسق مثل 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/cs/site.json b/web/content/cs/site.json index 39966a79..eb82692f 100644 --- a/web/content/cs/site.json +++ b/web/content/cs/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "id obrázkového formátu tak, jak je vypisuje tfg formats", "the password, in plain text": "heslo ve formě prostého textu", "any text": "libovolný text", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "datum, například 2024-02-29 nebo 2024-02-29T13:45:00+02:00, nebo none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "datum, například 2024-02-29 nebo 2024-02-29T13:45:00+02:00, nebo none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "okamžik v UTC, například 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/de/site.json b/web/content/de/site.json index 6f519a43..a5565d93 100644 --- a/web/content/de/site.json +++ b/web/content/de/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "die ID eines Bildformats, wie tfg formats sie auflistet", "the password, in plain text": "das Passwort im Klartext", "any text": "beliebiger Text", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "ein Datum wie 2024-02-29 oder 2024-02-29T13:45:00+02:00, oder none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "ein Datum wie 2024-02-29 oder 2024-02-29T13:45:00+02:00, oder none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "ein Zeitpunkt in UTC wie 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/en/site.json b/web/content/en/site.json index 949ed3b5..84768f49 100644 --- a/web/content/en/site.json +++ b/web/content/en/site.json @@ -270,7 +270,8 @@ "the id of a picture format, as tfg formats lists them": "the id of a picture format, as tfg formats lists them", "the password, in plain text": "the password, in plain text", "any text": "any text", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "a moment in UTC such as 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/es/site.json b/web/content/es/site.json index 61849251..91685873 100644 --- a/web/content/es/site.json +++ b/web/content/es/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "el id de un formato de imagen, tal como lo lista tfg formats", "the password, in plain text": "la contraseña, en texto plano", "any text": "cualquier texto", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "una fecha como 2024-02-29 o 2024-02-29T13:45:00+02:00, o none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "una fecha como 2024-02-29 o 2024-02-29T13:45:00+02:00, o none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "un instante en UTC como 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/fr/site.json b/web/content/fr/site.json index 8a8b132c..5dcf1080 100644 --- a/web/content/fr/site.json +++ b/web/content/fr/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "l'identifiant d'un format d'image, comme le liste tfg formats", "the password, in plain text": "le mot de passe, en clair", "any text": "n'importe quel texte", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "une date comme 2024-02-29 ou 2024-02-29T13:45:00+02:00, ou none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "une date comme 2024-02-29 ou 2024-02-29T13:45:00+02:00, ou none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "un instant en UTC comme 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/hi/site.json b/web/content/hi/site.json index 31e5eb47..3e6eb114 100644 --- a/web/content/hi/site.json +++ b/web/content/hi/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "किसी चित्र फ़ॉर्मैट का id, जैसा tfg formats सूचीबद्ध करता है", "the password, in plain text": "पासवर्ड, सादे टेक्स्ट में", "any text": "कोई भी टेक्स्ट", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "2024-02-29 या 2024-02-29T13:45:00+02:00 जैसी तारीख, या none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "2024-02-29 या 2024-02-29T13:45:00+02:00 जैसी तारीख, या none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "2026-08-01T12:00:00Z जैसा UTC का एक क्षण" }, "faq": [ { diff --git a/web/content/id/site.json b/web/content/id/site.json index 897189c5..6e122d5c 100644 --- a/web/content/id/site.json +++ b/web/content/id/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "id sebuah format gambar, seperti yang didaftar tfg formats", "the password, in plain text": "kata sandi, dalam teks biasa", "any text": "teks apa pun", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "tanggal seperti 2024-02-29 atau 2024-02-29T13:45:00+02:00, atau none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "tanggal seperti 2024-02-29 atau 2024-02-29T13:45:00+02:00, atau none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "waktu dalam UTC seperti 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/it/site.json b/web/content/it/site.json index 5250536d..5f6005bc 100644 --- a/web/content/it/site.json +++ b/web/content/it/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "l'id di un formato di immagine, come lo elenca tfg formats", "the password, in plain text": "la password, in chiaro", "any text": "qualsiasi testo", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "una data come 2024-02-29 o 2024-02-29T13:45:00+02:00, oppure none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "una data come 2024-02-29 o 2024-02-29T13:45:00+02:00, oppure none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "un istante in UTC come 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/ja/site.json b/web/content/ja/site.json index 7801c928..4c70ca54 100644 --- a/web/content/ja/site.json +++ b/web/content/ja/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "tfg formatsが一覧表示する画像形式のID", "the password, in plain text": "パスワード(平文)", "any text": "任意のテキスト", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "2024-02-29や2024-02-29T13:45:00+02:00のような日付、またはnone" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "2024-02-29や2024-02-29T13:45:00+02:00のような日付、またはnone", + "a moment in UTC such as 2026-08-01T12:00:00Z": "2026-08-01T12:00:00ZのようなUTCの時刻" }, "faq": [ { diff --git a/web/content/ko/site.json b/web/content/ko/site.json index 7646242a..2d9e719a 100644 --- a/web/content/ko/site.json +++ b/web/content/ko/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "tfg formats가 나열하는 이미지 형식의 id", "the password, in plain text": "암호(평문)", "any text": "임의의 텍스트", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "2024-02-29 또는 2024-02-29T13:45:00+02:00 같은 날짜, 또는 none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "2024-02-29 또는 2024-02-29T13:45:00+02:00 같은 날짜, 또는 none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "2026-08-01T12:00:00Z 같은 UTC 시각" }, "faq": [ { diff --git a/web/content/nl/site.json b/web/content/nl/site.json index 10f915de..3fd04c32 100644 --- a/web/content/nl/site.json +++ b/web/content/nl/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "de id van een afbeeldingsformaat, zoals tfg formats ze opsomt", "the password, in plain text": "het wachtwoord, als platte tekst", "any text": "willekeurige tekst", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "een datum zoals 2024-02-29 of 2024-02-29T13:45:00+02:00, of none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "een datum zoals 2024-02-29 of 2024-02-29T13:45:00+02:00, of none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "een moment in UTC zoals 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/pl/site.json b/web/content/pl/site.json index 36c65f9e..eff09c97 100644 --- a/web/content/pl/site.json +++ b/web/content/pl/site.json @@ -270,7 +270,8 @@ "the id of a picture format, as tfg formats lists them": "identyfikator formatu obrazka, tak jak wypisuje go tfg formats", "the password, in plain text": "hasło, zwykłym tekstem", "any text": "dowolny tekst", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "data, np. 2024-02-29 albo 2024-02-29T13:45:00+02:00, albo none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "data, np. 2024-02-29 albo 2024-02-29T13:45:00+02:00, albo none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "chwila w UTC, np. 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/pt-BR/site.json b/web/content/pt-BR/site.json index 25d23f62..1f5d7457 100644 --- a/web/content/pt-BR/site.json +++ b/web/content/pt-BR/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "o id de um formato de imagem, como o tfg formats lista", "the password, in plain text": "a senha, em texto simples", "any text": "qualquer texto", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "uma data como 2024-02-29 ou 2024-02-29T13:45:00+02:00, ou none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "uma data como 2024-02-29 ou 2024-02-29T13:45:00+02:00, ou none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "um instante em UTC como 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/ro/site.json b/web/content/ro/site.json index 5f52354d..d8d0c0db 100644 --- a/web/content/ro/site.json +++ b/web/content/ro/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "id-ul unui format de imagine, așa cum îl listează tfg formats", "the password, in plain text": "parola, în text simplu", "any text": "orice text", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "o dată precum 2024-02-29 sau 2024-02-29T13:45:00+02:00, sau none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "o dată precum 2024-02-29 sau 2024-02-29T13:45:00+02:00, sau none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "un moment în UTC precum 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/ru/site.json b/web/content/ru/site.json index c25ff1e9..534355a7 100644 --- a/web/content/ru/site.json +++ b/web/content/ru/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "id формата изображения в том виде, как его выводит tfg formats", "the password, in plain text": "пароль открытым текстом", "any text": "любой текст", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "дата вида 2024-02-29 или 2024-02-29T13:45:00+02:00, либо none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "дата вида 2024-02-29 или 2024-02-29T13:45:00+02:00, либо none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "момент в UTC вида 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/th/site.json b/web/content/th/site.json index 312882ce..7d8b27eb 100644 --- a/web/content/th/site.json +++ b/web/content/th/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "รหัสของรูปแบบรูปภาพ ตามที่ tfg formats แสดง", "the password, in plain text": "รหัสผ่านในรูปข้อความธรรมดา", "any text": "ข้อความใดก็ได้", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "วันที่ เช่น 2024-02-29 หรือ 2024-02-29T13:45:00+02:00 หรือ none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "วันที่ เช่น 2024-02-29 หรือ 2024-02-29T13:45:00+02:00 หรือ none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "เวลาใน UTC เช่น 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/tr/site.json b/web/content/tr/site.json index c5eaa729..1daae614 100644 --- a/web/content/tr/site.json +++ b/web/content/tr/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "tfg formats'ın listelediği gibi bir görüntü biçiminin kimliği", "the password, in plain text": "parola, düz metin olarak", "any text": "herhangi bir metin", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "2024-02-29 veya 2024-02-29T13:45:00+02:00 gibi bir tarih, veya none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "2024-02-29 veya 2024-02-29T13:45:00+02:00 gibi bir tarih, veya none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "2026-08-01T12:00:00Z gibi UTC'de bir an" }, "faq": [ { diff --git a/web/content/uk/site.json b/web/content/uk/site.json index b36bfe82..1e9ed324 100644 --- a/web/content/uk/site.json +++ b/web/content/uk/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "id формату зображення в тому вигляді, як його виводить tfg formats", "the password, in plain text": "пароль відкритим текстом", "any text": "будь-який текст", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "дата на кшталт 2024-02-29 або 2024-02-29T13:45:00+02:00, або none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "дата на кшталт 2024-02-29 або 2024-02-29T13:45:00+02:00, або none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "момент в UTC на кшталт 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/vi/site.json b/web/content/vi/site.json index 6cc88603..cc789f8e 100644 --- a/web/content/vi/site.json +++ b/web/content/vi/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "id của một định dạng hình ảnh, như tfg formats liệt kê", "the password, in plain text": "mật khẩu, ở dạng văn bản thuần", "any text": "văn bản bất kỳ", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "ngày như 2024-02-29 hoặc 2024-02-29T13:45:00+02:00, hoặc none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "ngày như 2024-02-29 hoặc 2024-02-29T13:45:00+02:00, hoặc none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "thời điểm theo UTC như 2026-08-01T12:00:00Z" }, "faq": [ { diff --git a/web/content/zh-Hans/site.json b/web/content/zh-Hans/site.json index dcba54f2..7b009dda 100644 --- a/web/content/zh-Hans/site.json +++ b/web/content/zh-Hans/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "图片格式的 id,与 tfg formats 列出的一致", "the password, in plain text": "明文密码", "any text": "任意文本", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "形如 2024-02-29 或 2024-02-29T13:45:00+02:00 的日期,或 none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "形如 2024-02-29 或 2024-02-29T13:45:00+02:00 的日期,或 none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "形如 2026-08-01T12:00:00Z 的 UTC 时刻" }, "faq": [ { diff --git a/web/content/zh-Hant/site.json b/web/content/zh-Hant/site.json index c0306d55..8bc54ffa 100644 --- a/web/content/zh-Hant/site.json +++ b/web/content/zh-Hant/site.json @@ -275,7 +275,8 @@ "the id of a picture format, as tfg formats lists them": "圖片格式的 id,與 tfg formats 列出的相同", "the password, in plain text": "明文密碼", "any text": "任意文字", - "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "形如 2024-02-29 或 2024-02-29T13:45:00+02:00 的日期,或 none" + "a date such as 2024-02-29 or 2024-02-29T13:45:00+02:00, or none": "形如 2024-02-29 或 2024-02-29T13:45:00+02:00 的日期,或 none", + "a moment in UTC such as 2026-08-01T12:00:00Z": "形如 2026-08-01T12:00:00Z 的 UTC 時刻" }, "faq": [ { diff --git a/web/public/ar/corrupt-test-files/index.html b/web/public/ar/corrupt-test-files/index.html index 80168e88..82e725bd 100644 --- a/web/public/ar/corrupt-test-files/index.html +++ b/web/public/ar/corrupt-test-files/index.html @@ -200,7 +200,7 @@

الملف التالف يبقى بالحجم الذي طلبته

tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

تُكتب الإعدادات بعد النقطتين. يمكن تكرار الخيار، وتُطبَّق أنواع التلف بالترتيب الذي تكتبه. ويعمل مع - كل واحدة من 31 صيغة. + كل واحدة من 32 صيغة.

diff --git a/web/public/ar/faq/index.html b/web/public/ar/faq/index.html index 4a08cfc6..0f14ce5e 100644 --- a/web/public/ar/faq/index.html +++ b/web/public/ar/faq/index.html @@ -205,7 +205,7 @@

الأسئلة الشائعة

ما الصيغ القادمة؟

-

7z وmp3. تعمل اليوم 31 صيغة من البداية إلى النهاية.

+

7z وmp3. تعمل اليوم 32 صيغة من البداية إلى النهاية.

@@ -276,7 +276,7 @@

الأسئلة الشائعة

{ "@type": "Question", "name": "ما الصيغ القادمة؟", - "acceptedAnswer": { "@type": "Answer", "text": "7z وmp3. تعمل اليوم 31 صيغة من البداية إلى النهاية." } + "acceptedAnswer": { "@type": "Answer", "text": "7z وmp3. تعمل اليوم 32 صيغة من البداية إلى النهاية." } }, { "@type": "Question", diff --git a/web/public/ar/formats/index.html b/web/public/ar/formats/index.html index d1be4742..24f9cda3 100644 --- a/web/public/ar/formats/index.html +++ b/web/public/ar/formats/index.html @@ -4,8 +4,8 @@ -31 صيغة ملف مدعومة - PDF وDOCX وPNG وZIP وغيرها - +32 صيغة ملف مدعومة - PDF وDOCX وPNG وZIP وغيرها + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
-

31 صيغة ملف، كلٌّ منها يُولَّد بحجم دقيق

+

32 صيغة ملف، كلٌّ منها يُولَّد بحجم دقيق

كلٌّ منها ملف حقيقي من تلك الصيغة. يُفتح في البرنامج الذي يملكه، وعدد بايتاته هو ما طلبته بالضبط. ولا واحد منها أصفار حشوية أُلصق بها امتداد. @@ -283,6 +283,14 @@

31 صيغة ملف، كلٌّ منها يُولَّد بحجم دقيق

full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -854,6 +862,51 @@

الإعدادات التي تقبلها كل صيغة

quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + لحظة بالتوقيت العالمي المنسق مثل 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 إدخال في الثانية + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + صحيح أو خاطئ + pdf pages diff --git a/web/public/ar/index.html b/web/public/ar/index.html index af070c2a..b4d11aaf 100644 --- a/web/public/ar/index.html +++ b/web/public/ar/index.html @@ -4,7 +4,7 @@ -مولّد ملفات الاختبار لضمان الجودة - حجم دقيق، 31 صيغة حقيقية +مولّد ملفات الاختبار لضمان الجودة - حجم دقيق، 32 صيغة حقيقية @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "مولّد ملفات اختبار مجاني ومفتوح المصدر لضمان الجودة. ينتج ملفات حقيقية بـ 31 صيغة وبحجم دقيق، ويكتب بيانًا يحدد كيف يجب أن يتفاعل النظام قيد الاختبار مع كل ملف.", + "description": "مولّد ملفات اختبار مجاني ومفتوح المصدر لضمان الجودة. ينتج ملفات حقيقية بـ 32 صيغة وبحجم دقيق، ويكتب بيانًا يحدد كيف يجب أن يتفاعل النظام قيد الاختبار مع كل ملف.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

ولّد ملفات اختبار حقيقية بحجم دقيق

- PDF وPNG وDOCX وZIP، 31 صيغة في المجمل، وكلٌّ منها ملف حقيقي + PDF وPNG وDOCX وZIP، 32 صيغة في المجمل، وكلٌّ منها ملف حقيقي يُفتح في البرنامج الذي يملكه، وبـالحجم الذي طلبته تمامًا. كما يدوّن كل تشغيل ما يجب أن يفعله تطبيقك بكل ملف. سطر أوامر ونافذة سطح مكتب، مجاني ومفتوح المصدر، ويعمل بالكامل على جهازك. @@ -179,7 +179,7 @@

ولّد ملفات اختبار حقيقية بحجم دقيق

  • - 31 + 32

    صيغة حقيقية، وكلٌّ منها يُفتح في البرنامج الذي يملكه

  • @@ -340,7 +340,7 @@

    مبني لمجموعة اختبارات تعمل دون إشراف

    اطلب 10485761 بايتًا واحصل على هذا العدد بالضبط. الحجم الذي لا تستطيع الصيغة بلوغه خطأ مع سبب، وليس ملفًا بحجم خاطئ أبدًا.

  • -

    31 صيغة حقيقية

    +

    32 صيغة حقيقية

    ليست أصفارًا حشوية بامتداد. يُفتح ملف PNG المولَّد في عارض الصور، ويُفتح DOCX في Word، ويُفك ضغط ZIP. وتُفحص كل صيغة بقرّاء مستقلين قبل إصدارها.

  • diff --git a/web/public/ar/presets/empty-and-minimal/index.html b/web/public/ar/presets/empty-and-minimal/index.html index b9e0d8a7..aff5998e 100644 --- a/web/public/ar/presets/empty-and-minimal/index.html +++ b/web/public/ar/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ أصغر ملفات اختبار صالحة وفارغة في كل صيغة - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

    ماذا في المجموعة؟

    - - - - + + + +
    الملفات33
    الأهداف في وصفته33
    الحجم الإجمالي40 496 B
    الصيغavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
    الملفات34
    الأهداف في وصفته34
    الحجم الإجمالي41 762 B
    الصيغavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
    @@ -192,7 +192,7 @@

    ماذا في المجموعة؟

    المتوقعالمعنىالملفات - acceptينبغي أن يقبل نظامك الملف.31 + acceptينبغي أن يقبل نظامك الملف.32 unspecifiedيعتمد على قواعد نظامك. أنت من يقرر، ثم تتحقق من أن ما يحدث هو ما قصدته.2 diff --git a/web/public/assets/social-preview.png b/web/public/assets/social-preview.png index d0ec5700..85d72cc6 100644 Binary files a/web/public/assets/social-preview.png and b/web/public/assets/social-preview.png differ diff --git a/web/public/corrupt-test-files/index.html b/web/public/corrupt-test-files/index.html index 3cf40aec..40ebbedc 100644 --- a/web/public/corrupt-test-files/index.html +++ b/web/public/corrupt-test-files/index.html @@ -203,7 +203,7 @@

    A damaged file is still the size you asked for

    tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

    Settings go after a colon. The flag can be repeated, and the damages are applied in the order you - write them. It works with every one of the 31 formats. + write them. It works with every one of the 32 formats.

    diff --git a/web/public/cs/faq/index.html b/web/public/cs/faq/index.html index 9c872863..3fc955b8 100644 --- a/web/public/cs/faq/index.html +++ b/web/public/cs/faq/index.html @@ -206,7 +206,7 @@

    Často kladené otázky

    Které formáty přijdou dál?

    -

    7z a mp3. Dnes funguje od začátku do konce 31 formátů.

    +

    7z a mp3. Dnes funguje od začátku do konce 32 formátů.

    @@ -277,7 +277,7 @@

    Často kladené otázky

    { "@type": "Question", "name": "Které formáty přijdou dál?", - "acceptedAnswer": { "@type": "Answer", "text": "7z a mp3. Dnes funguje od začátku do konce 31 formátů." } + "acceptedAnswer": { "@type": "Answer", "text": "7z a mp3. Dnes funguje od začátku do konce 32 formátů." } }, { "@type": "Question", diff --git a/web/public/cs/formaty/index.html b/web/public/cs/formaty/index.html index c455cda8..64f568e1 100644 --- a/web/public/cs/formaty/index.html +++ b/web/public/cs/formaty/index.html @@ -4,8 +4,8 @@ -31 podporovaných formátů souborů - PDF, DOCX, PNG, ZIP a další - +32 podporovaných formátů souborů - PDF, DOCX, PNG, ZIP a další + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
    -

    31 formátů souborů, každý generovaný v přesné velikosti

    +

    32 formátů souborů, každý generovaný v přesné velikosti

    Každý z nich je skutečný soubor toho formátu. Otevře se v programu, kam patří, a má přesně tolik bajtů, kolik jste požádali. Žádný není vycpávka z nul s přilepenou příponou. @@ -283,6 +283,14 @@

    31 formátů souborů, každý generovaný v přesné velikosti

    full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -855,6 +863,51 @@

    Nastavení, která každý formát přijímá

    quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + okamžik v UTC, například 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 položek za sekundu + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + pravda nebo nepravda + pdf pages diff --git a/web/public/cs/index.html b/web/public/cs/index.html index f472a9e3..90204563 100644 --- a/web/public/cs/index.html +++ b/web/public/cs/index.html @@ -4,7 +4,7 @@ -Generátor testovacích souborů - přesná velikost, 31 formátů +Generátor testovacích souborů - přesná velikost, 32 formátů @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Bezplatný open source generátor testovacích souborů pro QA. Vytváří skutečné soubory v 31 formátech v přesné velikosti a zapisuje manifest, který říká, jak má testovaný systém na každý z nich reagovat.", + "description": "Bezplatný open source generátor testovacích souborů pro QA. Vytváří skutečné soubory v 32 formátech v přesné velikosti a zapisuje manifest, který říká, jak má testovaný systém na každý z nich reagovat.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

    Generujte skutečné testovací soubory v přesné velikosti

    - PDF, PNG, DOCX, ZIP - celkem 31 formátů a každý je skutečný + PDF, PNG, DOCX, ZIP - celkem 32 formátů a každý je skutečný soubor, který se otevře v programu, kam patří, v přesně té velikosti, o kterou jste požádali. Každý běh také zapisuje, co má vaše aplikace s každým souborem udělat. Příkazový řádek a desktopové okno, zdarma a open source, celé na vašem počítači. @@ -179,7 +179,7 @@

    Generujte skutečné testovací soubory v přesné velikosti

    • - 31 + 32

      skutečných formátů, každý se otevře v programu, kam patří

    • @@ -341,7 +341,7 @@

      Vytvořeno pro sadu testů, která běží bez dozoru

      Požádejte o 10485761 bajtů a dostanete přesně tolik. Velikost, které formát nedosáhne, je chyba s důvodem, nikdy soubor špatné velikosti.

    • -

      31 skutečných formátů

      +

      32 skutečných formátů

      Ne vycpávkové nuly s příponou. Vygenerované PNG se otevře v prohlížeči obrázků, DOCX ve Wordu, ZIP se rozbalí. Každý formát je před vydáním ověřen nezávislými čtečkami.

    • diff --git a/web/public/cs/poskozene-testovaci-soubory/index.html b/web/public/cs/poskozene-testovaci-soubory/index.html index edeb3d6c..c4d7f0e1 100644 --- a/web/public/cs/poskozene-testovaci-soubory/index.html +++ b/web/public/cs/poskozene-testovaci-soubory/index.html @@ -202,7 +202,7 @@

      Poškozený soubor má stále velikost, kterou jste chtěli

      tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

      Nastavení se píše za dvojtečku. Přepínač lze opakovat a poškození se použijí v pořadí, v jakém je - zapíšete. Funguje s každým z 31 formátů. + zapíšete. Funguje s každým z 32 formátů.

      diff --git a/web/public/cs/predvolby/empty-and-minimal/index.html b/web/public/cs/predvolby/empty-and-minimal/index.html index acbaf2f2..3c9259ea 100644 --- a/web/public/cs/predvolby/empty-and-minimal/index.html +++ b/web/public/cs/predvolby/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Nejmenší platné a prázdné testovací soubory v každém formátu - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

      Co je v sadě?

      - - - - + + + +
      Soubory33
      Targety v receptu33
      Celková velikost40 496 B
      Formátyavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
      Soubory34
      Targety v receptu34
      Celková velikost41 762 B
      Formátyavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
      @@ -192,7 +192,7 @@

      Co je v sadě?

      OčekávánoVýznamSoubory - acceptVáš systém má soubor přijmout.31 + acceptVáš systém má soubor přijmout.32 unspecifiedZáleží na pravidlech vašeho systému. Rozhodnete vy, a pak ověříte, že to, co se stane, je to, co jste chtěli.2 diff --git a/web/public/de/beschaedigte-testdateien/index.html b/web/public/de/beschaedigte-testdateien/index.html index 9aa093c5..649ced91 100644 --- a/web/public/de/beschaedigte-testdateien/index.html +++ b/web/public/de/beschaedigte-testdateien/index.html @@ -205,7 +205,7 @@

      Eine beschädigte Datei hat trotzdem die verlangte Größe

      Einstellungen stehen nach einem Doppelpunkt. Die Option lässt sich wiederholen, und die Beschädigungen werden in der Reihenfolge angewendet, in der Sie sie schreiben. Es funktioniert - mit allen 31 Formaten. + mit allen 32 Formaten.

      diff --git a/web/public/de/faq/index.html b/web/public/de/faq/index.html index 1c639705..a0331f67 100644 --- a/web/public/de/faq/index.html +++ b/web/public/de/faq/index.html @@ -206,7 +206,7 @@

      Häufig gestellte Fragen

      Welche Formate kommen als Nächstes?

      -

      7z und mp3. 31 Formate funktionieren heute durchgängig.

      +

      7z und mp3. 32 Formate funktionieren heute durchgängig.

      @@ -277,7 +277,7 @@

      Häufig gestellte Fragen

      { "@type": "Question", "name": "Welche Formate kommen als Nächstes?", - "acceptedAnswer": { "@type": "Answer", "text": "7z und mp3. 31 Formate funktionieren heute durchgängig." } + "acceptedAnswer": { "@type": "Answer", "text": "7z und mp3. 32 Formate funktionieren heute durchgängig." } }, { "@type": "Question", diff --git a/web/public/de/formate/index.html b/web/public/de/formate/index.html index a09bed56..d10f1dec 100644 --- a/web/public/de/formate/index.html +++ b/web/public/de/formate/index.html @@ -4,8 +4,8 @@ -31 unterstützte Dateiformate - PDF, DOCX, PNG, ZIP und mehr - +32 unterstützte Dateiformate - PDF, DOCX, PNG, ZIP und mehr + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
      -

      31 Dateiformate, jedes in exakter Größe erzeugt

      +

      32 Dateiformate, jedes in exakter Größe erzeugt

      Jedes davon ist eine echte Datei dieses Formats. Sie öffnet sich in der Software, zu der sie gehört, und hat genau die Byte-Zahl, die du verlangt hast. Keines davon besteht aus @@ -284,6 +284,14 @@

      31 Dateiformate, jedes in exakter Größe erzeugt

      full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -857,6 +865,51 @@

      Einstellungen, die jedes Format kennt

      quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + ein Zeitpunkt in UTC wie 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 Einträge pro Sekunde + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + wahr oder falsch + pdf pages diff --git a/web/public/de/index.html b/web/public/de/index.html index 08fa0945..d7039f33 100644 --- a/web/public/de/index.html +++ b/web/public/de/index.html @@ -4,7 +4,7 @@ -Testdatei-Generator für QA - exakte Größe, 31 echte Formate +Testdatei-Generator für QA - exakte Größe, 32 echte Formate @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Ein kostenloser Open-Source-Generator für Testdateien in der QA. Er erzeugt echte Dateien in 31 Formaten in jeder exakten Größe und schreibt ein Manifest, das beschreibt, wie das getestete System auf jede Datei reagieren soll.", + "description": "Ein kostenloser Open-Source-Generator für Testdateien in der QA. Er erzeugt echte Dateien in 32 Formaten in jeder exakten Größe und schreibt ein Manifest, das beschreibt, wie das getestete System auf jede Datei reagieren soll.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

      Echte Testdateien in jeder exakten Größe erzeugen

      - PDF, PNG, DOCX, ZIP - insgesamt 31 Formate, und jedes ist + PDF, PNG, DOCX, ZIP - insgesamt 32 Formate, und jedes ist eine echte Datei, die sich in der Software öffnet, zu der sie gehört, in genau der Größe, die du verlangt hast. Jeder Lauf hält außerdem fest, was deine Anwendung mit jeder Datei tun soll. Kommandozeile und Desktop-Fenster, kostenlos und Open Source, vollständig @@ -180,7 +180,7 @@

      Echte Testdateien in jeder exakten Größe erzeugen

      • - 31 + 32

        echte Formate, jedes öffnet sich in der Software, zu der es gehört

      • @@ -345,7 +345,7 @@

        Gebaut für eine Testsuite, die unbeaufsichtigt läuft

        Verlange 10485761 Bytes und bekomme genau das. Eine Größe, die ein Format nicht erreichen kann, ist ein Fehler mit Begründung, nie eine Datei in der falschen Größe.

      • -

        31 echte Formate

        +

        32 echte Formate

        Keine aufgefüllten Nullen mit einer Endung. Ein erzeugtes PNG öffnet sich im Bildbetrachter, ein DOCX in Word, ein ZIP lässt sich entpacken. Jedes wird vor der Auslieferung mit unabhängigen Readern geprüft.

      • diff --git a/web/public/de/presets/empty-and-minimal/index.html b/web/public/de/presets/empty-and-minimal/index.html index f1a9e98e..df28017c 100644 --- a/web/public/de/presets/empty-and-minimal/index.html +++ b/web/public/de/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Kleinste gültige und leere Testdateien in jedem Format - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

        Was steckt im Set?

        - - - - + + + +
        Dateien33
        Targets in seinem Rezept33
        Gesamtgröße40 496 B
        Formateavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
        Dateien34
        Targets in seinem Rezept34
        Gesamtgröße41 762 B
        Formateavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
        @@ -192,7 +192,7 @@

        Was steckt im Set?

        ErwartetBedeutungDateien - acceptDein System soll die Datei annehmen.31 + acceptDein System soll die Datei annehmen.32 unspecifiedDas hängt von den Regeln deines Systems ab. Du entscheidest, dann prüfst du, ob das Ergebnis dem entspricht, was du gemeint hast.2 diff --git a/web/public/es/archivos-de-prueba-corruptos/index.html b/web/public/es/archivos-de-prueba-corruptos/index.html index 9aa91668..26a5e2ed 100644 --- a/web/public/es/archivos-de-prueba-corruptos/index.html +++ b/web/public/es/archivos-de-prueba-corruptos/index.html @@ -203,7 +203,7 @@

        Un archivo dañado conserva el tamaño que pediste

        tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

        Los ajustes van después de dos puntos. La opción se puede repetir, y los daños se aplican en el - orden en que los escribes. Funciona con cada uno de los 31 formatos. + orden en que los escribes. Funciona con cada uno de los 32 formatos.

        diff --git a/web/public/es/formatos/index.html b/web/public/es/formatos/index.html index c5c6524b..924b0eed 100644 --- a/web/public/es/formatos/index.html +++ b/web/public/es/formatos/index.html @@ -4,8 +4,8 @@ -31 formatos de archivo - PDF, DOCX, PNG, ZIP y más - +32 formatos de archivo - PDF, DOCX, PNG, ZIP y más + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
        -

        31 formatos de archivo, cada uno generado a un tamaño exacto

        +

        32 formatos de archivo, cada uno generado a un tamaño exacto

        Cada uno es un archivo real de ese formato. Se abre en el programa que le corresponde y tiene exactamente el número de bytes que pediste. Ninguno es relleno de ceros con @@ -284,6 +284,14 @@

        31 formatos de archivo, cada uno generado a un tamaño exacto

        full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -858,6 +866,51 @@

        Ajustes que admite cada formato

        quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + un instante en UTC como 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 entradas por segundo + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + verdadero o falso + pdf pages diff --git a/web/public/es/index.html b/web/public/es/index.html index 05f47640..96e34fa1 100644 --- a/web/public/es/index.html +++ b/web/public/es/index.html @@ -4,7 +4,7 @@ -Generador de archivos de prueba - tamaño exacto, 31 formatos +Generador de archivos de prueba - tamaño exacto, 32 formatos @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Un generador gratuito y de código abierto de archivos de prueba para QA. Produce archivos reales de 31 formatos al tamaño exacto y escribe un manifiesto que dice cómo debe reaccionar el sistema bajo prueba a cada uno.", + "description": "Un generador gratuito y de código abierto de archivos de prueba para QA. Produce archivos reales de 32 formatos al tamaño exacto y escribe un manifiesto que dice cómo debe reaccionar el sistema bajo prueba a cada uno.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

        Genera archivos de prueba reales al tamaño exacto

        - PDF, PNG, DOCX, ZIP - 31 formatos en total, y cada uno es un + PDF, PNG, DOCX, ZIP - 32 formatos en total, y cada uno es un archivo real que se abre en el programa que le corresponde, con exactamente el tamaño que pediste. Cada ejecución también anota qué debe hacer tu aplicación con cada archivo. Línea de comandos y ventana de escritorio, gratis y de código abierto, funcionando por @@ -180,7 +180,7 @@

        Genera archivos de prueba reales al tamaño exacto

        • - 31 + 32

          formatos reales, cada uno se abre en el programa que le corresponde

        • @@ -345,7 +345,7 @@

          Pensado para un conjunto de pruebas que corre sin supervisión

          Pide 10485761 bytes y obtén exactamente eso. Un tamaño que un formato no puede alcanzar es un error con un motivo, nunca un archivo del tamaño equivocado.

        • -

          31 formatos reales

          +

          32 formatos reales

          No ceros de relleno con una extensión. Un PNG generado se abre en un visor de imágenes, un DOCX se abre en Word, un ZIP se extrae. Cada uno se comprueba con lectores independientes antes de publicarse.

        • diff --git a/web/public/es/preguntas-frecuentes/index.html b/web/public/es/preguntas-frecuentes/index.html index 1582d4de..fb96f594 100644 --- a/web/public/es/preguntas-frecuentes/index.html +++ b/web/public/es/preguntas-frecuentes/index.html @@ -206,7 +206,7 @@

          Preguntas frecuentes

          ¿Qué formatos vienen después?

          -

          7z y mp3. Hoy funcionan de principio a fin 31 formatos.

          +

          7z y mp3. Hoy funcionan de principio a fin 32 formatos.

          @@ -277,7 +277,7 @@

          Preguntas frecuentes

          { "@type": "Question", "name": "¿Qué formatos vienen después?", - "acceptedAnswer": { "@type": "Answer", "text": "7z y mp3. Hoy funcionan de principio a fin 31 formatos." } + "acceptedAnswer": { "@type": "Answer", "text": "7z y mp3. Hoy funcionan de principio a fin 32 formatos." } }, { "@type": "Question", diff --git a/web/public/es/presets/empty-and-minimal/index.html b/web/public/es/presets/empty-and-minimal/index.html index a7e67f85..a927a526 100644 --- a/web/public/es/presets/empty-and-minimal/index.html +++ b/web/public/es/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Archivos de prueba válidos mínimos y vacíos en cada formato - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -179,10 +179,10 @@

          ¿Qué hay en el conjunto?

          - - - - + + + +
          Archivos33
          Targets en su receta33
          Tamaño total40 496 B
          Formatosavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
          Archivos34
          Targets en su receta34
          Tamaño total41 762 B
          Formatosavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
          @@ -193,7 +193,7 @@

          ¿Qué hay en el conjunto?

          EsperadoSignificadoArchivos - acceptTu sistema debe aceptar el archivo.31 + acceptTu sistema debe aceptar el archivo.32 unspecifiedDepende de las reglas de tu sistema. Tú decides y después compruebas que lo que ocurre es lo que querías.2 diff --git a/web/public/faq/index.html b/web/public/faq/index.html index be33bf19..2da39b73 100644 --- a/web/public/faq/index.html +++ b/web/public/faq/index.html @@ -205,7 +205,7 @@

          Frequently asked questions

          Which formats are coming next?

          -

          7z and mp3. 31 formats work end to end today.

          +

          7z and mp3. 32 formats work end to end today.

          @@ -276,7 +276,7 @@

          Frequently asked questions

          { "@type": "Question", "name": "Which formats are coming next?", - "acceptedAnswer": { "@type": "Answer", "text": "7z and mp3. 31 formats work end to end today." } + "acceptedAnswer": { "@type": "Answer", "text": "7z and mp3. 32 formats work end to end today." } }, { "@type": "Question", diff --git a/web/public/formats/index.html b/web/public/formats/index.html index 5e7b9020..b1f7c5df 100644 --- a/web/public/formats/index.html +++ b/web/public/formats/index.html @@ -4,8 +4,8 @@ -31 Supported File Formats - PDF, DOCX, PNG, ZIP and More - +32 Supported File Formats - PDF, DOCX, PNG, ZIP and More + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
          -

          31 file formats, every one generated at an exact size

          +

          32 file formats, every one generated at an exact size

          Each of these is a real file of that format. It opens in the software that owns it, and it is exactly the number of bytes you asked for. None of them is padded zeros with an extension @@ -284,6 +284,14 @@

          31 file formats, every one generated at an exact size

          full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -856,6 +864,51 @@

          Settings each format accepts

          quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + a moment in UTC such as 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 entries per second + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + true or false + pdf pages diff --git a/web/public/fr/faq/index.html b/web/public/fr/faq/index.html index 1189fb1c..f0b8876a 100644 --- a/web/public/fr/faq/index.html +++ b/web/public/fr/faq/index.html @@ -206,7 +206,7 @@

          Questions fréquentes

          Quels formats arrivent ensuite ?

          -

          7z et mp3. 31 formats fonctionnent de bout en bout aujourd'hui.

          +

          7z et mp3. 32 formats fonctionnent de bout en bout aujourd'hui.

          @@ -277,7 +277,7 @@

          Questions fréquentes

          { "@type": "Question", "name": "Quels formats arrivent ensuite ?", - "acceptedAnswer": { "@type": "Answer", "text": "7z et mp3. 31 formats fonctionnent de bout en bout aujourd\u0027hui." } + "acceptedAnswer": { "@type": "Answer", "text": "7z et mp3. 32 formats fonctionnent de bout en bout aujourd\u0027hui." } }, { "@type": "Question", diff --git a/web/public/fr/fichiers-de-test-corrompus/index.html b/web/public/fr/fichiers-de-test-corrompus/index.html index b65a855c..a476edc7 100644 --- a/web/public/fr/fichiers-de-test-corrompus/index.html +++ b/web/public/fr/fichiers-de-test-corrompus/index.html @@ -204,7 +204,7 @@

          Un fichier abîmé garde la taille demandée

          tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

          Les réglages se placent après deux-points. L'option peut être répétée, et les dommages s'appliquent - dans l'ordre où vous les écrivez. Ça marche avec chacun des 31 formats. + dans l'ordre où vous les écrivez. Ça marche avec chacun des 32 formats.

          diff --git a/web/public/fr/formats/index.html b/web/public/fr/formats/index.html index 6592127a..dc51fad2 100644 --- a/web/public/fr/formats/index.html +++ b/web/public/fr/formats/index.html @@ -4,8 +4,8 @@ -31 formats de fichiers - PDF, DOCX, PNG, ZIP et plus - +32 formats de fichiers - PDF, DOCX, PNG, ZIP et plus + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
          -

          31 formats de fichiers, chacun généré à une taille exacte

          +

          32 formats de fichiers, chacun généré à une taille exacte

          Chacun est un vrai fichier de ce format. Il s'ouvre dans le logiciel qui lui correspond et fait exactement le nombre d'octets que vous avez demandé. Aucun n'est du remplissage @@ -284,6 +284,14 @@

          31 formats de fichiers, chacun généré à une taille exacte

          full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -858,6 +866,51 @@

          Réglages que chaque format accepte

          quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + un instant en UTC comme 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 entrées par seconde + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + vrai ou faux + pdf pages diff --git a/web/public/fr/index.html b/web/public/fr/index.html index 4b326489..91e0a966 100644 --- a/web/public/fr/index.html +++ b/web/public/fr/index.html @@ -4,7 +4,7 @@ -Générateur de fichiers de test - taille exacte, 31 vrais formats +Générateur de fichiers de test - taille exacte, 32 vrais formats @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Un générateur gratuit et open source de fichiers de test pour la QA. Il produit de vrais fichiers dans 31 formats, à la taille exacte, et écrit un manifeste qui dit comment le système testé doit réagir à chacun.", + "description": "Un générateur gratuit et open source de fichiers de test pour la QA. Il produit de vrais fichiers dans 32 formats, à la taille exacte, et écrit un manifeste qui dit comment le système testé doit réagir à chacun.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

          Générez de vrais fichiers de test à la taille exacte

          - PDF, PNG, DOCX, ZIP - 31 formats au total, et chacun est un + PDF, PNG, DOCX, ZIP - 32 formats au total, et chacun est un vrai fichier qui s'ouvre dans le logiciel qui lui correspond, à la taille exacte que vous avez demandée. Chaque exécution note aussi ce que votre application doit faire de chaque fichier. Ligne de commande et fenêtre de bureau, gratuit et open source, entièrement sur @@ -180,7 +180,7 @@

          Générez de vrais fichiers de test à la taille exacte

          • - 31 + 32

            vrais formats, chacun s'ouvrant dans le logiciel qui lui correspond

          • @@ -345,7 +345,7 @@

            Conçu pour une suite qui tourne sans surveillance

            Demandez 10485761 octets et obtenez exactement cela. Une taille qu'un format ne peut pas atteindre est une erreur avec une raison, jamais un fichier de mauvaise taille.

          • -

            31 vrais formats

            +

            32 vrais formats

            Pas des zéros de remplissage avec une extension. Un PNG généré s'ouvre dans une visionneuse d'images, un DOCX s'ouvre dans Word, un ZIP se décompresse. Chacun est vérifié avec des lecteurs indépendants avant d'être livré.

          • diff --git a/web/public/fr/prereglages/empty-and-minimal/index.html b/web/public/fr/prereglages/empty-and-minimal/index.html index ff6c52fe..4ba336dd 100644 --- a/web/public/fr/prereglages/empty-and-minimal/index.html +++ b/web/public/fr/prereglages/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Fichiers de test valides minimaux et vides, dans chaque format - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -179,10 +179,10 @@

            Que contient le jeu ?

            - - - - + + + +
            Fichiers33
            Cibles dans sa recette33
            Taille totale40 496 B
            Formatsavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
            Fichiers34
            Cibles dans sa recette34
            Taille totale41 762 B
            Formatsavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
            @@ -193,7 +193,7 @@

            Que contient le jeu ?

            AttenduSignificationFichiers - acceptVotre système doit accepter le fichier.31 + acceptVotre système doit accepter le fichier.32 unspecifiedCela dépend des règles de votre système. Vous décidez, puis vous vérifiez que ce qui se passe est ce que vous aviez voulu.2 diff --git a/web/public/hi/corrupt-test-files/index.html b/web/public/hi/corrupt-test-files/index.html index e5b71f5d..ef3e99da 100644 --- a/web/public/hi/corrupt-test-files/index.html +++ b/web/public/hi/corrupt-test-files/index.html @@ -202,7 +202,7 @@

            खराब फ़ाइल का आकार वही रहता ह tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

            सेटिंग कोलन के बाद लिखी जाती है। विकल्प दोहराया जा सकता है, और बिगाड़ आपके लिखे क्रम में लागू होते - हैं। यह सभी 31 फ़ॉर्मैट के साथ चलता है। + हैं। यह सभी 32 फ़ॉर्मैट के साथ चलता है।

            diff --git a/web/public/hi/faq/index.html b/web/public/hi/faq/index.html index 992f4834..329eca3b 100644 --- a/web/public/hi/faq/index.html +++ b/web/public/hi/faq/index.html @@ -205,7 +205,7 @@

            अक्सर पूछे जाने वाले सवाल

            आगे कौन से फ़ॉर्मैट आ रहे हैं?

            -

            7z और mp3। आज 31 फ़ॉर्मैट शुरू से अंत तक काम करते हैं।

            +

            7z और mp3। आज 32 फ़ॉर्मैट शुरू से अंत तक काम करते हैं।

            @@ -276,7 +276,7 @@

            अक्सर पूछे जाने वाले सवाल

            { "@type": "Question", "name": "आगे कौन से फ़ॉर्मैट आ रहे हैं?", - "acceptedAnswer": { "@type": "Answer", "text": "7z और mp3। आज 31 फ़ॉर्मैट शुरू से अंत तक काम करते हैं।" } + "acceptedAnswer": { "@type": "Answer", "text": "7z और mp3। आज 32 फ़ॉर्मैट शुरू से अंत तक काम करते हैं।" } }, { "@type": "Question", diff --git a/web/public/hi/formats/index.html b/web/public/hi/formats/index.html index 2bd540cc..a6d4dd70 100644 --- a/web/public/hi/formats/index.html +++ b/web/public/hi/formats/index.html @@ -4,8 +4,8 @@ -31 समर्थित फ़ाइल फ़ॉर्मैट - PDF, DOCX, PNG, ZIP और अन्य - +32 समर्थित फ़ाइल फ़ॉर्मैट - PDF, DOCX, PNG, ZIP और अन्य + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
            -

            31 फ़ाइल फ़ॉर्मैट, हर एक सटीक आकार में बनाया गया

            +

            32 फ़ाइल फ़ॉर्मैट, हर एक सटीक आकार में बनाया गया

            इनमें से हर एक उस फ़ॉर्मैट की असली फ़ाइल है। वह अपने सॉफ़्टवेयर में खुलती है और उसमें ठीक उतने बाइट हैं जितने आपने माँगे। इनमें से कोई भी एक्सटेंशन चिपकाए गए भराव के शून्य नहीं @@ -284,6 +284,14 @@

            31 फ़ाइल फ़ॉर्मैट, हर एक सटीक आ full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -856,6 +864,51 @@

            हर फ़ॉर्मैट की स्वीकार की जा quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + 2026-08-01T12:00:00Z जैसा UTC का एक क्षण + + + + rate + 1 - 1000000 प्रति सेकंड प्रविष्टियाँ + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + सही या गलत + pdf pages diff --git a/web/public/hi/index.html b/web/public/hi/index.html index 8da97d28..6ba72565 100644 --- a/web/public/hi/index.html +++ b/web/public/hi/index.html @@ -4,7 +4,7 @@ -QA के लिए टेस्ट फ़ाइल जनरेटर - सटीक आकार, 31 असली फ़ॉर्मैट +QA के लिए टेस्ट फ़ाइल जनरेटर - सटीक आकार, 32 असली फ़ॉर्मैट @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "QA के लिए मुफ़्त और ओपन सोर्स टेस्ट फ़ाइल जनरेटर। यह 31 फ़ॉर्मैट में सटीक आकार की असली फ़ाइलें बनाता है और एक मैनिफ़ेस्ट लिखता है जो बताता है कि जाँचे जा रहे सिस्टम को हर फ़ाइल पर क्या प्रतिक्रिया देनी चाहिए।", + "description": "QA के लिए मुफ़्त और ओपन सोर्स टेस्ट फ़ाइल जनरेटर। यह 32 फ़ॉर्मैट में सटीक आकार की असली फ़ाइलें बनाता है और एक मैनिफ़ेस्ट लिखता है जो बताता है कि जाँचे जा रहे सिस्टम को हर फ़ाइल पर क्या प्रतिक्रिया देनी चाहिए।", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

            सटीक आकार की असली टेस्ट फ़ाइलें बनाएँ

            - PDF, PNG, DOCX, ZIP - कुल 31 फ़ॉर्मैट, और हर एक असली फ़ाइल है + PDF, PNG, DOCX, ZIP - कुल 32 फ़ॉर्मैट, और हर एक असली फ़ाइल है जो अपने सॉफ़्टवेयर में खुलती है, ठीक उसी आकार में जो आपने माँगा। हर रन यह भी लिखता है कि आपके एप्लिकेशन को हर फ़ाइल के साथ क्या करना चाहिए। कमांड लाइन और डेस्कटॉप विंडो, मुफ़्त और ओपन सोर्स, पूरी तरह आपकी मशीन पर चलता है। @@ -179,7 +179,7 @@

            सटीक आकार की असली टेस्ट फ़ाइ
            • - 31 + 32

              असली फ़ॉर्मैट, हर एक अपने सॉफ़्टवेयर में खुलता है

            • @@ -341,7 +341,7 @@

              बिना निगरानी के चलने वाले सू

              10485761 बाइट माँगें और ठीक वही पाएँ। जिस आकार तक कोई फ़ॉर्मैट नहीं पहुँच सकता, वह कारण सहित त्रुटि है, गलत आकार की फ़ाइल कभी नहीं।

            • -

              31 असली फ़ॉर्मैट

              +

              32 असली फ़ॉर्मैट

              एक्सटेंशन वाले भराव के शून्य नहीं। बनाई गई PNG इमेज व्यूअर में खुलती है, DOCX Word में खुलता है, ZIP खुल जाता है। हर एक को भेजने से पहले स्वतंत्र रीडरों से जाँचा जाता है।

            • diff --git a/web/public/hi/presets/empty-and-minimal/index.html b/web/public/hi/presets/empty-and-minimal/index.html index 739c6bff..a19cc07a 100644 --- a/web/public/hi/presets/empty-and-minimal/index.html +++ b/web/public/hi/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ हर फ़ॉर्मैट की सबसे छोटी वैध और खाली टेस्ट फ़ाइलें - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

              सेट में क्या है?

              - - - - + + + +
              फ़ाइलें33
              इसकी रेसिपी में टार्गेट33
              कुल आकार40 496 B
              फ़ॉर्मैटavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
              फ़ाइलें34
              इसकी रेसिपी में टार्गेट34
              कुल आकार41 762 B
              फ़ॉर्मैटavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
              @@ -192,7 +192,7 @@

              सेट में क्या है?

              अपेक्षितअर्थफ़ाइलें - acceptआपके सिस्टम को फ़ाइल मंज़ूर करनी चाहिए।31 + acceptआपके सिस्टम को फ़ाइल मंज़ूर करनी चाहिए।32 unspecifiedयह आपके सिस्टम के नियमों पर निर्भर है। आप तय करें, फिर जाँचें कि जो होता है वही है जो आप चाहते थे।2 diff --git a/web/public/id/faq/index.html b/web/public/id/faq/index.html index abaa077e..51e4906b 100644 --- a/web/public/id/faq/index.html +++ b/web/public/id/faq/index.html @@ -206,7 +206,7 @@

              Pertanyaan yang sering diajukan

              Format apa yang akan hadir berikutnya?

              -

              7z dan mp3. Saat ini 31 format bekerja dari ujung ke ujung.

              +

              7z dan mp3. Saat ini 32 format bekerja dari ujung ke ujung.

              @@ -277,7 +277,7 @@

              Pertanyaan yang sering diajukan

              { "@type": "Question", "name": "Format apa yang akan hadir berikutnya?", - "acceptedAnswer": { "@type": "Answer", "text": "7z dan mp3. Saat ini 31 format bekerja dari ujung ke ujung." } + "acceptedAnswer": { "@type": "Answer", "text": "7z dan mp3. Saat ini 32 format bekerja dari ujung ke ujung." } }, { "@type": "Question", diff --git a/web/public/id/file-uji-rusak/index.html b/web/public/id/file-uji-rusak/index.html index 3d8be848..57896c97 100644 --- a/web/public/id/file-uji-rusak/index.html +++ b/web/public/id/file-uji-rusak/index.html @@ -203,7 +203,7 @@

              File yang rusak tetap berukuran seperti yang Anda minta

              tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

              Pengaturan ditulis setelah titik dua. Opsi ini bisa diulang, dan kerusakan diterapkan menurut urutan - yang Anda tulis. Berlaku untuk semua 31 format. + yang Anda tulis. Berlaku untuk semua 32 format.

              diff --git a/web/public/id/format/index.html b/web/public/id/format/index.html index c4fcdb3c..2225ae1e 100644 --- a/web/public/id/format/index.html +++ b/web/public/id/format/index.html @@ -4,8 +4,8 @@ -31 Format File yang Didukung - PDF, DOCX, PNG, ZIP, dan Lainnya - +32 Format File yang Didukung - PDF, DOCX, PNG, ZIP, dan Lainnya + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
              -

              31 format file, masing-masing dibuat dengan ukuran tepat

              +

              32 format file, masing-masing dibuat dengan ukuran tepat

              Masing-masing adalah file asli dari format itu. File terbuka di aplikasi yang memilikinya dan persis berjumlah byte yang Anda minta. Tidak ada yang berupa nol pengisi dengan @@ -284,6 +284,14 @@

              31 format file, masing-masing dibuat dengan ukuran tepat

              full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -856,6 +864,51 @@

              Pengaturan yang diterima setiap format

              quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + waktu dalam UTC seperti 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 entri per detik + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + benar atau salah + pdf pages diff --git a/web/public/id/index.html b/web/public/id/index.html index fdd3446a..ef509b8c 100644 --- a/web/public/id/index.html +++ b/web/public/id/index.html @@ -4,7 +4,7 @@ -Generator File Uji untuk QA - Ukuran Tepat, 31 Format Asli +Generator File Uji untuk QA - Ukuran Tepat, 32 Format Asli @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Generator file uji untuk QA yang gratis dan open source. Alat ini membuat file asli dalam 31 format dengan ukuran tepat dan menulis manifes yang menyatakan bagaimana sistem yang diuji harus bereaksi terhadap tiap file.", + "description": "Generator file uji untuk QA yang gratis dan open source. Alat ini membuat file asli dalam 32 format dengan ukuran tepat dan menulis manifes yang menyatakan bagaimana sistem yang diuji harus bereaksi terhadap tiap file.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

              Buat file uji asli dengan ukuran tepat

              - PDF, PNG, DOCX, ZIP - 31 format seluruhnya, dan setiap file + PDF, PNG, DOCX, ZIP - 32 format seluruhnya, dan setiap file adalah file asli yang terbuka di aplikasi yang memilikinya, dengan ukuran persis seperti yang Anda minta. Setiap run juga mencatat apa yang harus dilakukan aplikasi Anda terhadap tiap file. Baris perintah dan jendela desktop, gratis dan open source, bekerja @@ -180,7 +180,7 @@

              Buat file uji asli dengan ukuran tepat

              • - 31 + 32

                format asli, masing-masing terbuka di aplikasi yang memilikinya

              • @@ -344,7 +344,7 @@

                Dibuat untuk suite yang berjalan tanpa pengawasan

                Minta 10485761 byte dan dapatkan persis itu. Ukuran yang tidak dapat dicapai format adalah galat dengan alasan, tidak pernah file berukuran salah.

              • -

                31 format asli

                +

                32 format asli

                Bukan nol pengisi dengan ekstensi. PNG yang dihasilkan terbuka di penampil gambar, DOCX terbuka di Word, ZIP bisa diekstrak. Masing-masing diperiksa dengan pembaca independen sebelum dirilis.

              • diff --git a/web/public/id/preset/empty-and-minimal/index.html b/web/public/id/preset/empty-and-minimal/index.html index f75da46c..64e18d54 100644 --- a/web/public/id/preset/empty-and-minimal/index.html +++ b/web/public/id/preset/empty-and-minimal/index.html @@ -5,7 +5,7 @@ File Uji Valid Terkecil dan Kosong di Setiap Format - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -179,10 +179,10 @@

                Apa isi set?

                - - - - + + + +
                File33
                Target dalam resepnya33
                Ukuran total40 496 B
                Formatavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                File34
                Target dalam resepnya34
                Ukuran total41 762 B
                Formatavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                @@ -193,7 +193,7 @@

                Apa isi set?

                DiharapkanArtiFile - acceptSistem Anda harus menerima file ini.31 + acceptSistem Anda harus menerima file ini.32 unspecifiedTergantung pada aturan sistem Anda. Anda yang memutuskan, lalu memeriksa apakah yang terjadi sesuai maksud Anda.2 diff --git a/web/public/index.html b/web/public/index.html index da2f9357..ffa41da4 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -4,7 +4,7 @@ -Test File Generator for QA - Exact Size, 31 Real Formats +Test File Generator for QA - Exact Size, 32 Real Formats @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "A free and open source generator of test files for QA. It produces real files of 31 formats at any exact size and writes a manifest saying how the system under test should react to each one.", + "description": "A free and open source generator of test files for QA. It produces real files of 32 formats at any exact size and writes a manifest saying how the system under test should react to each one.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                Generate real test files at any exact size

                - PDF, PNG, DOCX, ZIP - 31 formats in all, and every one is a + PDF, PNG, DOCX, ZIP - 32 formats in all, and every one is a real file that opens in the software that owns it, at exactly the size you asked for. Each run also writes down what your application is supposed to do with each file. Command line and desktop window, free and open source, working entirely on your machine. @@ -179,7 +179,7 @@

                Generate real test files at any exact size

                • - 31 + 32

                  real formats, each one opening in the software that owns it

                • @@ -342,7 +342,7 @@

                  Built for a suite that runs unattended

                  Ask for 10485761 bytes and get exactly that. A size a format cannot reach is an error with a reason, never a file of the wrong size.

                • -

                  31 real formats

                  +

                  32 real formats

                  Not padded zeros with an extension. A generated PNG opens in an image viewer, a DOCX opens in Word, a ZIP extracts. Each one is checked against independent readers before it ships.

                • diff --git a/web/public/it/domande-frequenti/index.html b/web/public/it/domande-frequenti/index.html index da9c9377..3c66a58c 100644 --- a/web/public/it/domande-frequenti/index.html +++ b/web/public/it/domande-frequenti/index.html @@ -206,7 +206,7 @@

                  Domande frequenti

                  Quali formati arrivano dopo?

                  -

                  7z e mp3. Oggi 31 formati funzionano da un capo all'altro.

                  +

                  7z e mp3. Oggi 32 formati funzionano da un capo all'altro.

                  @@ -277,7 +277,7 @@

                  Domande frequenti

                  { "@type": "Question", "name": "Quali formati arrivano dopo?", - "acceptedAnswer": { "@type": "Answer", "text": "7z e mp3. Oggi 31 formati funzionano da un capo all\u0027altro." } + "acceptedAnswer": { "@type": "Answer", "text": "7z e mp3. Oggi 32 formati funzionano da un capo all\u0027altro." } }, { "@type": "Question", diff --git a/web/public/it/file-di-test-corrotti/index.html b/web/public/it/file-di-test-corrotti/index.html index f385b52a..fe7c1947 100644 --- a/web/public/it/file-di-test-corrotti/index.html +++ b/web/public/it/file-di-test-corrotti/index.html @@ -203,7 +203,7 @@

                  Un file danneggiato ha ancora la dimensione richiesta

                  tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                  Le impostazioni vanno dopo i due punti. L'opzione si può ripetere, e i danni vengono applicati - nell'ordine in cui li scrivi. Funziona con ognuno dei 31 formati. + nell'ordine in cui li scrivi. Funziona con ognuno dei 32 formati.

                  diff --git a/web/public/it/formati/index.html b/web/public/it/formati/index.html index a692ff9f..d68f00aa 100644 --- a/web/public/it/formati/index.html +++ b/web/public/it/formati/index.html @@ -4,8 +4,8 @@ -31 formati di file supportati - PDF, DOCX, PNG, ZIP e altri - +32 formati di file supportati - PDF, DOCX, PNG, ZIP e altri + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                  -

                  31 formati di file, ognuno generato a una dimensione esatta

                  +

                  32 formati di file, ognuno generato a una dimensione esatta

                  Ognuno è un file reale di quel formato. Si apre nel programma che gli appartiene ed è esattamente il numero di byte che hai chiesto. Nessuno è riempimento di zeri con un'estensione @@ -284,6 +284,14 @@

                  31 formati di file, ognuno generato a una dimensione esatta

                  full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -858,6 +866,51 @@

                  Impostazioni che ogni formato accetta

                  quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + un istante in UTC come 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 voci al secondo + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + vero o falso + pdf pages diff --git a/web/public/it/index.html b/web/public/it/index.html index 19010eeb..428ad58d 100644 --- a/web/public/it/index.html +++ b/web/public/it/index.html @@ -4,7 +4,7 @@ -Generatore di file di test - dimensione esatta, 31 formati reali +Generatore di file di test - dimensione esatta, 32 formati reali @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Un generatore gratuito e open source di file di test per la QA. Produce file reali in 31 formati della dimensione esatta e scrive un manifest che dice come il sistema sotto test deve reagire a ciascuno.", + "description": "Un generatore gratuito e open source di file di test per la QA. Produce file reali in 32 formati della dimensione esatta e scrive un manifest che dice come il sistema sotto test deve reagire a ciascuno.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                  Genera file di test reali della dimensione esatta

                  - PDF, PNG, DOCX, ZIP - 31 formati in tutto, e ognuno è un file + PDF, PNG, DOCX, ZIP - 32 formati in tutto, e ognuno è un file reale che si apre nel programma che gli appartiene, di esattamente la dimensione che hai chiesto. Ogni esecuzione annota anche cosa la tua applicazione deve fare con ogni file. Riga di comando e finestra desktop, gratuito e open source, interamente sulla tua macchina. @@ -179,7 +179,7 @@

                  Genera file di test reali della dimensione esatta

                  • - 31 + 32

                    formati reali, ognuno si apre nel programma che gli appartiene

                  • @@ -344,7 +344,7 @@

                    Costruito per una suite che gira senza supervisione

                    Chiedi 10485761 byte e ottieni esattamente quelli. Una dimensione che un formato non può raggiungere è un errore con un motivo, mai un file della dimensione sbagliata.

                  • -

                    31 formati reali

                    +

                    32 formati reali

                    Non zeri di riempimento con un'estensione. Un PNG generato si apre in un visualizzatore di immagini, un DOCX si apre in Word, uno ZIP si estrae. Ognuno è verificato con lettori indipendenti prima del rilascio.

                  • diff --git a/web/public/it/preset/empty-and-minimal/index.html b/web/public/it/preset/empty-and-minimal/index.html index 9af0ba2e..c716f6b3 100644 --- a/web/public/it/preset/empty-and-minimal/index.html +++ b/web/public/it/preset/empty-and-minimal/index.html @@ -5,7 +5,7 @@ File di test validi minimi e vuoti in ogni formato - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -179,10 +179,10 @@

                    Cosa c'è nel set?

                    - - - - + + + +
                    File33
                    Target nella sua ricetta33
                    Dimensione totale40 496 B
                    Formatiavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                    File34
                    Target nella sua ricetta34
                    Dimensione totale41 762 B
                    Formatiavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                    @@ -193,7 +193,7 @@

                    Cosa c'è nel set?

                    AttesoSignificatoFile - acceptIl tuo sistema deve accettare il file.31 + acceptIl tuo sistema deve accettare il file.32 unspecifiedDipende dalle regole del tuo sistema. Decidi tu, poi controlli che ciò che accade sia ciò che intendevi.2 diff --git a/web/public/ja/corrupt-test-files/index.html b/web/public/ja/corrupt-test-files/index.html index 068c4dc1..aa9b78f4 100644 --- a/web/public/ja/corrupt-test-files/index.html +++ b/web/public/ja/corrupt-test-files/index.html @@ -191,7 +191,7 @@

                    破損したファイルも、求めたサイズのままです

                    tfg generate --format zip --size 5mb --damage truncate:missing=30 --out ./cut tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                    - 設定はコロンの後ろに書きます。オプションは繰り返せて、破損は書いた順に適用されます。31種類すべての形式で使えます。 + 設定はコロンの後ろに書きます。オプションは繰り返せて、破損は書いた順に適用されます。32種類すべての形式で使えます。

                    diff --git a/web/public/ja/faq/index.html b/web/public/ja/faq/index.html index f94f3bb7..5359fbbe 100644 --- a/web/public/ja/faq/index.html +++ b/web/public/ja/faq/index.html @@ -204,7 +204,7 @@

                    よくある質問

                    次に対応する形式は何ですか。

                    -

                    7zとmp3です。現在、31種類の形式がエンドツーエンドで動作します。

                    +

                    7zとmp3です。現在、32種類の形式がエンドツーエンドで動作します。

                    @@ -275,7 +275,7 @@

                    よくある質問

                    { "@type": "Question", "name": "次に対応する形式は何ですか。", - "acceptedAnswer": { "@type": "Answer", "text": "7zとmp3です。現在、31種類の形式がエンドツーエンドで動作します。" } + "acceptedAnswer": { "@type": "Answer", "text": "7zとmp3です。現在、32種類の形式がエンドツーエンドで動作します。" } }, { "@type": "Question", diff --git a/web/public/ja/formats/index.html b/web/public/ja/formats/index.html index ca3c1ddc..cbcda979 100644 --- a/web/public/ja/formats/index.html +++ b/web/public/ja/formats/index.html @@ -4,8 +4,8 @@ -対応する31種類のファイル形式 - PDF、DOCX、PNG、ZIPほか - +対応する32種類のファイル形式 - PDF、DOCX、PNG、ZIPほか + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                    -

                    31種類のファイル形式、どれも正確なサイズで生成

                    +

                    32種類のファイル形式、どれも正確なサイズで生成

                    どれもその形式の本物のファイルです。対応するソフトウェアで開け、要求したバイト数とぴったり一致します。拡張子を貼り付けただけのゼロ埋めはひとつもありません。

                    @@ -282,6 +282,14 @@

                    31種類のファイル形式、どれも正確なサイズで生成

                    full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -848,6 +856,51 @@

                    形式ごとに受け付ける設定

                    quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + 2026-08-01T12:00:00ZのようなUTCの時刻 + + + + rate + 1 - 1000000 1秒あたりのエントリ数 + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + 真または偽 + pdf pages diff --git a/web/public/ja/index.html b/web/public/ja/index.html index 122481d4..ca30fa92 100644 --- a/web/public/ja/index.html +++ b/web/public/ja/index.html @@ -4,7 +4,7 @@ -テストファイル生成ツール - 正確なサイズ、31種類の本物の形式 +テストファイル生成ツール - 正確なサイズ、32種類の本物の形式 @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "QA向けの無料オープンソースのテストファイル生成ツールです。31種類の形式で正確なサイズの本物のファイルを作り、テスト対象のシステムが各ファイルにどう反応すべきかを示すマニフェストを書き出します。", + "description": "QA向けの無料オープンソースのテストファイル生成ツールです。32種類の形式で正確なサイズの本物のファイルを作り、テスト対象のシステムが各ファイルにどう反応すべきかを示すマニフェストを書き出します。", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                    正確なサイズの本物のテストファイルを生成

                    - PDF、PNG、DOCX、ZIPなど、全31種類の形式に対応し、どれも対応するソフトウェアで開ける本物のファイルで、サイズは要求したとおりちょうどです。各実行では、アプリが各ファイルをどう扱うべきかも書き出します。コマンドラインとデスクトップウィンドウの両方に対応し、無料のオープンソースで、すべてあなたのマシン上で動きます。 + PDF、PNG、DOCX、ZIPなど、全32種類の形式に対応し、どれも対応するソフトウェアで開ける本物のファイルで、サイズは要求したとおりちょうどです。各実行では、アプリが各ファイルをどう扱うべきかも書き出します。コマンドラインとデスクトップウィンドウの両方に対応し、無料のオープンソースで、すべてあなたのマシン上で動きます。

                    @@ -176,7 +176,7 @@

                    正確なサイズの本物のテストファイルを生成

                    • - 31 + 32

                      種類の本物の形式、どれも対応するソフトウェアで開けます

                    • @@ -328,7 +328,7 @@

                      無人で動くテストスイートのために作られています

                      10485761バイトを要求すれば、ちょうどその大きさになります。形式が到達できないサイズは理由付きのエラーになり、サイズの違うファイルが作られることはありません。

                    • -

                      31種類の本物の形式

                      +

                      32種類の本物の形式

                      拡張子を付けただけのゼロ埋めではありません。生成したPNGは画像ビューアーで開け、DOCXはWordで開け、ZIPは展開できます。どれも出荷前に独立したリーダーで検証されます。

                    • diff --git a/web/public/ja/presets/empty-and-minimal/index.html b/web/public/ja/presets/empty-and-minimal/index.html index 1a8815cd..f47ff873 100644 --- a/web/public/ja/presets/empty-and-minimal/index.html +++ b/web/public/ja/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ 全形式の最小の有効ファイルと空ファイル - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -176,10 +176,10 @@

                      セットには何が入っていますか。

                      - - - - + + + +
                      ファイル数33
                      レシピ内のターゲット数33
                      合計サイズ40 496 B
                      形式avif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                      ファイル数34
                      レシピ内のターゲット数34
                      合計サイズ41 762 B
                      形式avif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                      @@ -190,7 +190,7 @@

                      セットには何が入っていますか。

                      期待意味ファイル数 - acceptシステムはこのファイルを受け入れるべきです。31 + acceptシステムはこのファイルを受け入れるべきです。32 unspecifiedシステムのルール次第です。あなたが決め、実際に起きることが意図どおりかを確認してください。2 diff --git a/web/public/ko/corrupt-test-files/index.html b/web/public/ko/corrupt-test-files/index.html index f6acc4c2..0acbe7ea 100644 --- a/web/public/ko/corrupt-test-files/index.html +++ b/web/public/ko/corrupt-test-files/index.html @@ -196,7 +196,7 @@

                      손상된 파일도 요청한 크기를 유지합니다

                      tfg generate --format zip --size 5mb --damage truncate:missing=30 --out ./cut tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                      - 설정은 콜론 뒤에 씁니다. 옵션은 반복할 수 있고, 손상은 쓴 순서대로 적용됩니다. 31개 형식 모두에서 동작합니다. + 설정은 콜론 뒤에 씁니다. 옵션은 반복할 수 있고, 손상은 쓴 순서대로 적용됩니다. 32개 형식 모두에서 동작합니다.

                      diff --git a/web/public/ko/faq/index.html b/web/public/ko/faq/index.html index da595cc8..62877d64 100644 --- a/web/public/ko/faq/index.html +++ b/web/public/ko/faq/index.html @@ -205,7 +205,7 @@

                      자주 묻는 질문

                      다음에는 어떤 형식이 추가되나요?

                      -

                      7z와 mp3입니다. 현재 31가지 형식이 처음부터 끝까지 동작합니다.

                      +

                      7z와 mp3입니다. 현재 32가지 형식이 처음부터 끝까지 동작합니다.

                      @@ -276,7 +276,7 @@

                      자주 묻는 질문

                      { "@type": "Question", "name": "다음에는 어떤 형식이 추가되나요?", - "acceptedAnswer": { "@type": "Answer", "text": "7z와 mp3입니다. 현재 31가지 형식이 처음부터 끝까지 동작합니다." } + "acceptedAnswer": { "@type": "Answer", "text": "7z와 mp3입니다. 현재 32가지 형식이 처음부터 끝까지 동작합니다." } }, { "@type": "Question", diff --git a/web/public/ko/formats/index.html b/web/public/ko/formats/index.html index d80d6740..0320d823 100644 --- a/web/public/ko/formats/index.html +++ b/web/public/ko/formats/index.html @@ -4,8 +4,8 @@ -지원하는 파일 형식 31종 - PDF, DOCX, PNG, ZIP 등 - +지원하는 파일 형식 32종 - PDF, DOCX, PNG, ZIP 등 + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                      -

                      파일 형식 31가지, 모두 정확한 크기로 생성됩니다

                      +

                      파일 형식 32가지, 모두 정확한 크기로 생성됩니다

                      이들은 모두 해당 형식의 실제 파일입니다. 해당 소프트웨어에서 열리며 요청한 바이트 수와 정확히 같습니다. 확장자만 붙인 채운 0은 하나도 없습니다.

                      @@ -282,6 +282,14 @@

                      파일 형식 31가지, 모두 정확한 크기로 생성됩니다

                      full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -849,6 +857,51 @@

                      각 형식이 받는 설정

                      quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + 2026-08-01T12:00:00Z 같은 UTC 시각 + + + + rate + 1 - 1000000 초당 항목 수 + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + 참 또는 거짓 + pdf pages diff --git a/web/public/ko/index.html b/web/public/ko/index.html index 5525d26c..f965b1f5 100644 --- a/web/public/ko/index.html +++ b/web/public/ko/index.html @@ -4,7 +4,7 @@ -테스트 파일 생성기 - 정확한 크기, 실제 형식 31종 +테스트 파일 생성기 - 정확한 크기, 실제 형식 32종 @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "QA를 위한 무료 오픈 소스 테스트 파일 생성기입니다. 31가지 형식의 정확한 크기의 실제 파일을 만들고, 테스트 대상 시스템이 각 파일에 어떻게 반응해야 하는지 알려 주는 매니페스트를 작성합니다.", + "description": "QA를 위한 무료 오픈 소스 테스트 파일 생성기입니다. 32가지 형식의 정확한 크기의 실제 파일을 만들고, 테스트 대상 시스템이 각 파일에 어떻게 반응해야 하는지 알려 주는 매니페스트를 작성합니다.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                      정확한 크기의 실제 테스트 파일을 만드세요

                      - PDF, PNG, DOCX, ZIP 등 모두 31가지 형식이며, 모두 해당 소프트웨어에서 열리는 실제 파일로 + PDF, PNG, DOCX, ZIP 등 모두 32가지 형식이며, 모두 해당 소프트웨어에서 열리는 실제 파일로 요청한 크기와 정확히 같습니다. 매 실행마다 애플리케이션이 각 파일을 어떻게 처리해야 하는지도 함께 기록합니다. 명령줄과 데스크톱 창, 무료 오픈 소스이며, 모두 여러분의 컴퓨터에서 동작합니다.

                      @@ -178,7 +178,7 @@

                      정확한 크기의 실제 테스트 파일을 만드세요

                      • - 31 + 32

                        가지 실제 형식, 각각 해당 소프트웨어에서 열립니다

                      • @@ -335,7 +335,7 @@

                        무인으로 실행되는 테스트 묶음을 위해 만들었습니다

                        10485761바이트를 요청하면 정확히 그 크기가 나옵니다. 형식이 도달할 수 없는 크기는 이유가 붙은 오류가 되며, 크기가 틀린 파일이 되는 일은 없습니다.

                      • -

                        실제 형식 31가지

                        +

                        실제 형식 32가지

                        확장자만 붙인 채운 0이 아닙니다. 생성된 PNG는 이미지 뷰어에서 열리고, DOCX는 Word에서 열리며, ZIP은 풀립니다. 모든 형식은 출시 전에 독립적인 리더로 검증됩니다.

                      • diff --git a/web/public/ko/presets/empty-and-minimal/index.html b/web/public/ko/presets/empty-and-minimal/index.html index d7ddfc95..10e79b60 100644 --- a/web/public/ko/presets/empty-and-minimal/index.html +++ b/web/public/ko/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ 모든 형식의 가장 작은 유효 파일과 빈 파일 - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -177,10 +177,10 @@

                        세트에는 무엇이 들어 있나요?

                        - - - - + + + +
                        파일 수33
                        레시피의 타깃 수33
                        총 크기40 496 B
                        형식avif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                        파일 수34
                        레시피의 타깃 수34
                        총 크기41 762 B
                        형식avif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                        @@ -191,7 +191,7 @@

                        세트에는 무엇이 들어 있나요?

                        기대값의미파일 수 - accept시스템은 이 파일을 받아야 합니다.31 + accept시스템은 이 파일을 받아야 합니다.32 unspecified시스템의 규칙에 따라 다릅니다. 직접 결정한 뒤 실제 일어나는 일이 의도한 것인지 확인하세요.2 diff --git a/web/public/nl/beschadigde-testbestanden/index.html b/web/public/nl/beschadigde-testbestanden/index.html index 7e3b3de0..946235d2 100644 --- a/web/public/nl/beschadigde-testbestanden/index.html +++ b/web/public/nl/beschadigde-testbestanden/index.html @@ -205,7 +205,7 @@

                        Een beschadigd bestand heeft nog steeds de grootte die je vroeg

                        Instellingen komen achter een dubbele punt. De optie mag herhaald worden, en de beschadigingen worden toegepast in de volgorde waarin je ze schrijft. Het werkt met elk van de - 31 formaten. + 32 formaten.

                        diff --git a/web/public/nl/formaten/index.html b/web/public/nl/formaten/index.html index 052b08a4..36617965 100644 --- a/web/public/nl/formaten/index.html +++ b/web/public/nl/formaten/index.html @@ -4,8 +4,8 @@ -31 ondersteunde bestandsformaten - PDF, DOCX, PNG, ZIP en meer - +32 ondersteunde bestandsformaten - PDF, DOCX, PNG, ZIP en meer + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                        -

                        31 bestandsformaten, elk gegenereerd in een exacte grootte

                        +

                        32 bestandsformaten, elk gegenereerd in een exacte grootte

                        Elk daarvan is een echt bestand van dat formaat. Het opent in het programma waartoe het behoort en is precies het aantal bytes dat je vroeg. Geen enkele is opgevulde nullen met een @@ -284,6 +284,14 @@

                        31 bestandsformaten, elk gegenereerd in een exacte grootte

                        full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -858,6 +866,51 @@

                        Instellingen die elk formaat accepteert

                        quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + een moment in UTC zoals 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 items per seconde + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + waar of onwaar + pdf pages diff --git a/web/public/nl/index.html b/web/public/nl/index.html index 66ca7683..f37e992e 100644 --- a/web/public/nl/index.html +++ b/web/public/nl/index.html @@ -4,7 +4,7 @@ -Testbestanden genereren - exacte grootte, 31 echte formaten +Testbestanden genereren - exacte grootte, 32 echte formaten @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Een gratis open source generator van testbestanden voor QA. Hij maakt echte bestanden in 31 formaten in elke exacte grootte en schrijft een manifest dat zegt hoe het geteste systeem op elk bestand moet reageren.", + "description": "Een gratis open source generator van testbestanden voor QA. Hij maakt echte bestanden in 32 formaten in elke exacte grootte en schrijft een manifest dat zegt hoe het geteste systeem op elk bestand moet reageren.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                        Genereer echte testbestanden in elke exacte grootte

                        - PDF, PNG, DOCX, ZIP - 31 formaten in totaal, en elk is een + PDF, PNG, DOCX, ZIP - 32 formaten in totaal, en elk is een echt bestand dat opent in het programma waartoe het behoort, in precies de grootte die je vroeg. Elke run schrijft ook op wat je applicatie met elk bestand moet doen. Opdrachtregel en bureaubladvenster, gratis en open source, volledig op je eigen machine. @@ -179,7 +179,7 @@

                        Genereer echte testbestanden in elke exacte grootte

                        • - 31 + 32

                          echte formaten, elk opent in het programma waartoe het behoort

                        • @@ -342,7 +342,7 @@

                          Gebouwd voor een suite die onbeheerd draait

                          Vraag 10485761 bytes en krijg precies dat. Een grootte die een formaat niet kan halen is een fout met een reden, nooit een bestand van de verkeerde grootte.

                        • -

                          31 echte formaten

                          +

                          32 echte formaten

                          Geen opgevulde nullen met een extensie. Een gegenereerde PNG opent in een afbeeldingsviewer, een DOCX opent in Word, een ZIP pakt uit. Elk wordt vóór levering gecontroleerd met onafhankelijke lezers.

                        • diff --git a/web/public/nl/presets/empty-and-minimal/index.html b/web/public/nl/presets/empty-and-minimal/index.html index a51142f9..bd55fdef 100644 --- a/web/public/nl/presets/empty-and-minimal/index.html +++ b/web/public/nl/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Kleinste geldige en lege testbestanden in elk formaat - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -179,10 +179,10 @@

                          Wat zit er in de set?

                          - - - - + + + +
                          Bestanden33
                          Targets in het recept33
                          Totale grootte40 496 B
                          Formatenavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                          Bestanden34
                          Targets in het recept34
                          Totale grootte41 762 B
                          Formatenavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                          @@ -193,7 +193,7 @@

                          Wat zit er in de set?

                          VerwachtBetekenisBestanden - acceptJe systeem moet het bestand aannemen.31 + acceptJe systeem moet het bestand aannemen.32 unspecifiedHet hangt af van de regels van je systeem. Jij beslist, en controleert daarna of wat er gebeurt is wat je bedoelde.2 diff --git a/web/public/nl/veelgestelde-vragen/index.html b/web/public/nl/veelgestelde-vragen/index.html index 39056593..c51b40cd 100644 --- a/web/public/nl/veelgestelde-vragen/index.html +++ b/web/public/nl/veelgestelde-vragen/index.html @@ -206,7 +206,7 @@

                          Veelgestelde vragen

                          Welke formaten komen er nog?

                          -

                          7z en mp3. Vandaag werken 31 formaten van begin tot eind.

                          +

                          7z en mp3. Vandaag werken 32 formaten van begin tot eind.

                          @@ -277,7 +277,7 @@

                          Veelgestelde vragen

                          { "@type": "Question", "name": "Welke formaten komen er nog?", - "acceptedAnswer": { "@type": "Answer", "text": "7z en mp3. Vandaag werken 31 formaten van begin tot eind." } + "acceptedAnswer": { "@type": "Answer", "text": "7z en mp3. Vandaag werken 32 formaten van begin tot eind." } }, { "@type": "Question", diff --git a/web/public/pl/faq/index.html b/web/public/pl/faq/index.html index 8ea172a5..60e2244b 100644 --- a/web/public/pl/faq/index.html +++ b/web/public/pl/faq/index.html @@ -206,7 +206,7 @@

                          Najczęstsze pytania

                          Jakie formaty są następne w kolejce?

                          -

                          7z i mp3. Formatów działających dziś od początku do końca jest 31.

                          +

                          7z i mp3. Formatów działających dziś od początku do końca jest 32.

                          @@ -277,7 +277,7 @@

                          Najczęstsze pytania

                          { "@type": "Question", "name": "Jakie formaty są następne w kolejce?", - "acceptedAnswer": { "@type": "Answer", "text": "7z i mp3. Formatów działających dziś od początku do końca jest 31." } + "acceptedAnswer": { "@type": "Answer", "text": "7z i mp3. Formatów działających dziś od początku do końca jest 32." } }, { "@type": "Question", diff --git a/web/public/pl/formaty/index.html b/web/public/pl/formaty/index.html index dc2bc8af..b161b2af 100644 --- a/web/public/pl/formaty/index.html +++ b/web/public/pl/formaty/index.html @@ -4,7 +4,7 @@ -31 formatów plików testowych - PDF, DOCX, PNG, ZIP +32 formatów plików testowych - PDF, DOCX, PNG, ZIP @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -144,7 +144,7 @@
                          -

                          31 formatów plików, każdy generowany o dokładnym rozmiarze

                          +

                          32 formatów plików, każdy generowany o dokładnym rozmiarze

                          Każdy z nich to prawdziwy plik tego formatu. Otwiera się w programie, do którego należy, i ma dokładnie tyle bajtów, ile zamówiłeś. Żaden nie jest zerami z doklejonym rozszerzeniem. @@ -283,6 +283,14 @@

                          31 formatów plików, każdy generowany o dokładnym rozmiarze

                          full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -856,6 +864,51 @@

                          Ustawienia, które przyjmuje każdy format

                          quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + chwila w UTC, np. 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 wpisów na sekundę + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + prawda albo fałsz + pdf pages diff --git a/web/public/pl/index.html b/web/public/pl/index.html index 6014c781..6cc4fc10 100644 --- a/web/public/pl/index.html +++ b/web/public/pl/index.html @@ -4,7 +4,7 @@ -Generator plików testowych o zadanym rozmiarze - 31 formatów +Generator plików testowych o zadanym rozmiarze - 32 formatów @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Darmowy generator plików testowych dla QA o otwartym kodzie. Tworzy prawdziwe pliki w 31 formatach o dokładnie zadanym rozmiarze i zapisuje manifest mówiący, jak testowany system ma na każdy z nich zareagować.", + "description": "Darmowy generator plików testowych dla QA o otwartym kodzie. Tworzy prawdziwe pliki w 32 formatach o dokładnie zadanym rozmiarze i zapisuje manifest mówiący, jak testowany system ma na każdy z nich zareagować.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                          Generuj pliki testowe o zadanym rozmiarze

                          - PDF, PNG, DOCX, ZIP - razem 31 formatów, a każdy to + PDF, PNG, DOCX, ZIP - razem 32 formatów, a każdy to prawdziwy plik, który otwiera się w programie, do którego należy, i ma dokładnie taki rozmiar, o jaki poprosisz. Każdy przebieg zapisuje też, co Twoja aplikacja ma z każdym plikiem zrobić. Wiersz poleceń i okno, darmowe i otwarte, działające wyłącznie na Twojej maszynie. @@ -179,7 +179,7 @@

                          Generuj pliki testowe o zadanym rozmiarze

                          • - 31 + 32

                            prawdziwych formatów, każdy otwiera się w programie, do którego należy

                          • @@ -342,7 +342,7 @@

                            Zbudowane pod zestaw, który chodzi bez nadzoru

                            Poproś o 10485761 bajtów i tyle dostaniesz. Rozmiar nieosiągalny dla formatu to błąd z powodem, nigdy plik o innym rozmiarze.

                          • -

                            31 prawdziwych formatów

                            +

                            32 prawdziwych formatów

                            Nie zera z doklejonym rozszerzeniem. Wygenerowany PNG otwiera się w przeglądarce obrazów, DOCX w Wordzie, a ZIP się rozpakowuje. Każdy format jest sprawdzany niezależnym czytnikiem, zanim trafi do wydania.

                          • diff --git a/web/public/pl/presety/empty-and-minimal/index.html b/web/public/pl/presety/empty-and-minimal/index.html index 04caa9f3..73af5bee 100644 --- a/web/public/pl/presety/empty-and-minimal/index.html +++ b/web/public/pl/presety/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Najmniejsze poprawne i puste pliki w każdym formacie - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

                            Co jest w zestawie?

                            - - - - + + + +
                            Pliki33
                            Cele w przepisie33
                            Łączny rozmiar40 496 B
                            Formatyavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                            Pliki34
                            Cele w przepisie34
                            Łączny rozmiar41 762 B
                            Formatyavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                            @@ -192,7 +192,7 @@

                            Co jest w zestawie?

                            OczekiwanieZnaczeniePliki - acceptSystem powinien przyjąć plik.31 + acceptSystem powinien przyjąć plik.32 unspecifiedZależy od reguł Twojego systemu. Ty decydujesz, a potem sprawdzasz, czy dzieje się to, co zamierzałeś.2 diff --git a/web/public/pl/uszkodzone-pliki-testowe/index.html b/web/public/pl/uszkodzone-pliki-testowe/index.html index af5bbd7a..e604b9ae 100644 --- a/web/public/pl/uszkodzone-pliki-testowe/index.html +++ b/web/public/pl/uszkodzone-pliki-testowe/index.html @@ -204,7 +204,7 @@

                            Uszkodzony plik ma nadal rozmiar, o który prosiłeś

                            tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                            Ustawienia podaje się po dwukropku. Flagę można powtarzać, a uszkodzenia są stosowane w kolejności, - w jakiej je zapiszesz. Działa z każdym z 31 formatów. + w jakiej je zapiszesz. Działa z każdym z 32 formatów.

                            diff --git a/web/public/presets/empty-and-minimal/index.html b/web/public/presets/empty-and-minimal/index.html index 246b4608..ee42355c 100644 --- a/web/public/presets/empty-and-minimal/index.html +++ b/web/public/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Smallest Valid and Empty Test Files in Every Format - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

                            What is in the set?

                            - - - - + + + +
                            Files33
                            Targets in its recipe33
                            Total size40 496 B
                            Formatsavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                            Files34
                            Targets in its recipe34
                            Total size41 762 B
                            Formatsavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                            @@ -192,7 +192,7 @@

                            What is in the set?

                            ExpectedMeaningFiles - acceptYour system should take the file.31 + acceptYour system should take the file.32 unspecifiedIt depends on the rules of your system. You decide, then check that what happens is what you meant.2 diff --git a/web/public/pt-br/arquivos-de-teste-corrompidos/index.html b/web/public/pt-br/arquivos-de-teste-corrompidos/index.html index acadc891..d515c732 100644 --- a/web/public/pt-br/arquivos-de-teste-corrompidos/index.html +++ b/web/public/pt-br/arquivos-de-teste-corrompidos/index.html @@ -203,7 +203,7 @@

                            Um arquivo danificado continua com o tamanho que você pediu

                            tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                            As configurações vão depois de dois-pontos. A opção pode ser repetida, e os danos são aplicados na - ordem em que você os escreve. Funciona com cada um dos 31 formatos. + ordem em que você os escreve. Funciona com cada um dos 32 formatos.

                            diff --git a/web/public/pt-br/formatos/index.html b/web/public/pt-br/formatos/index.html index 8ac8f273..f94c1f8b 100644 --- a/web/public/pt-br/formatos/index.html +++ b/web/public/pt-br/formatos/index.html @@ -4,8 +4,8 @@ -31 formatos de arquivo - PDF, DOCX, PNG, ZIP e mais - +32 formatos de arquivo - PDF, DOCX, PNG, ZIP e mais + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                            -

                            31 formatos de arquivo, cada um gerado em um tamanho exato

                            +

                            32 formatos de arquivo, cada um gerado em um tamanho exato

                            Cada um é um arquivo real daquele formato. Abre no programa a que pertence e tem exatamente o número de bytes que você pediu. Nenhum é preenchimento de zeros com uma extensão @@ -284,6 +284,14 @@

                            31 formatos de arquivo, cada um gerado em um tamanho exato

                            full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -857,6 +865,51 @@

                            Configurações que cada formato aceita

                            quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + um instante em UTC como 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 entradas por segundo + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + verdadeiro ou falso + pdf pages diff --git a/web/public/pt-br/index.html b/web/public/pt-br/index.html index 0268a831..b611c609 100644 --- a/web/public/pt-br/index.html +++ b/web/public/pt-br/index.html @@ -4,7 +4,7 @@ -Gerador de arquivos de teste - tamanho exato, 31 formatos reais +Gerador de arquivos de teste - tamanho exato, 32 formatos reais @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Um gerador gratuito e de código aberto de arquivos de teste para QA. Produz arquivos reais de 31 formatos no tamanho exato e escreve um manifesto que diz como o sistema sob teste deve reagir a cada um.", + "description": "Um gerador gratuito e de código aberto de arquivos de teste para QA. Produz arquivos reais de 32 formatos no tamanho exato e escreve um manifesto que diz como o sistema sob teste deve reagir a cada um.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                            Gere arquivos de teste reais no tamanho exato

                            - PDF, PNG, DOCX, ZIP - 31 formatos no total, e cada um é um + PDF, PNG, DOCX, ZIP - 32 formatos no total, e cada um é um arquivo real que abre no programa a que pertence, com exatamente o tamanho que você pediu. Cada execução também registra o que sua aplicação deve fazer com cada arquivo. Linha de comando e janela de desktop, gratuito e de código aberto, funcionando inteiramente na @@ -180,7 +180,7 @@

                            Gere arquivos de teste reais no tamanho exato

                            • - 31 + 32

                              formatos reais, cada um abre no programa a que pertence

                            • @@ -345,7 +345,7 @@

                              Feito para uma suíte que roda sem supervisão

                              Peça 10485761 bytes e receba exatamente isso. Um tamanho que um formato não alcança é um erro com um motivo, nunca um arquivo de tamanho errado.

                            • -

                              31 formatos reais

                              +

                              32 formatos reais

                              Não são zeros de preenchimento com uma extensão. Um PNG gerado abre em um visualizador de imagens, um DOCX abre no Word, um ZIP extrai. Cada um é verificado com leitores independentes antes de ser liberado.

                            • diff --git a/web/public/pt-br/perguntas-frequentes/index.html b/web/public/pt-br/perguntas-frequentes/index.html index 7d69dce3..83546169 100644 --- a/web/public/pt-br/perguntas-frequentes/index.html +++ b/web/public/pt-br/perguntas-frequentes/index.html @@ -206,7 +206,7 @@

                              Perguntas frequentes

                              Quais formatos vêm a seguir?

                              -

                              7z e mp3. Hoje 31 formatos funcionam de ponta a ponta.

                              +

                              7z e mp3. Hoje 32 formatos funcionam de ponta a ponta.

                              @@ -277,7 +277,7 @@

                              Perguntas frequentes

                              { "@type": "Question", "name": "Quais formatos vêm a seguir?", - "acceptedAnswer": { "@type": "Answer", "text": "7z e mp3. Hoje 31 formatos funcionam de ponta a ponta." } + "acceptedAnswer": { "@type": "Answer", "text": "7z e mp3. Hoje 32 formatos funcionam de ponta a ponta." } }, { "@type": "Question", diff --git a/web/public/pt-br/presets/empty-and-minimal/index.html b/web/public/pt-br/presets/empty-and-minimal/index.html index 38445eec..c0b432e8 100644 --- a/web/public/pt-br/presets/empty-and-minimal/index.html +++ b/web/public/pt-br/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Menores arquivos de teste válidos e vazios em cada formato - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

                              O que há no conjunto?

                              - - - - + + + +
                              Arquivos33
                              Targets na sua receita33
                              Tamanho total40 496 B
                              Formatosavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                              Arquivos34
                              Targets na sua receita34
                              Tamanho total41 762 B
                              Formatosavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                              @@ -192,7 +192,7 @@

                              O que há no conjunto?

                              EsperadoSignificadoArquivos - acceptSeu sistema deve aceitar o arquivo.31 + acceptSeu sistema deve aceitar o arquivo.32 unspecifiedDepende das regras do seu sistema. Você decide e depois confere se o que acontece é o que você queria.2 diff --git a/web/public/ro/fisiere-de-test-corupte/index.html b/web/public/ro/fisiere-de-test-corupte/index.html index ba6c171b..6c4bfb46 100644 --- a/web/public/ro/fisiere-de-test-corupte/index.html +++ b/web/public/ro/fisiere-de-test-corupte/index.html @@ -204,7 +204,7 @@

                              Un fișier deteriorat are în continuare mărimea cerută

                              tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                              Setările se scriu după două puncte. Opțiunea poate fi repetată, iar deteriorările se aplică în - ordinea în care le scrii. Funcționează cu fiecare dintre cele 31 formate. + ordinea în care le scrii. Funcționează cu fiecare dintre cele 32 formate.

                              diff --git a/web/public/ro/formate/index.html b/web/public/ro/formate/index.html index 1a4022a4..155c857d 100644 --- a/web/public/ro/formate/index.html +++ b/web/public/ro/formate/index.html @@ -4,8 +4,8 @@ -31 formate de fișiere - PDF, DOCX, PNG, ZIP și altele - +32 formate de fișiere - PDF, DOCX, PNG, ZIP și altele + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                              -

                              31 formate de fișiere, fiecare generat la o dimensiune exactă

                              +

                              32 formate de fișiere, fiecare generat la o dimensiune exactă

                              Fiecare este un fișier real al acelui format. Se deschide în programul căruia îi aparține și are exact numărul de octeți pe care l-ai cerut. Niciunul nu e umplutură de zerouri cu @@ -284,6 +284,14 @@

                              31 formate de fișiere, fiecare generat la o dimensiune exactă

                              full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -857,6 +865,51 @@

                              Setările pe care le acceptă fiecare format

                              quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + un moment în UTC precum 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 intrări pe secundă + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + adevărat sau fals + pdf pages diff --git a/web/public/ro/index.html b/web/public/ro/index.html index 1230e786..698da962 100644 --- a/web/public/ro/index.html +++ b/web/public/ro/index.html @@ -4,7 +4,7 @@ -Generator de fișiere de test - dimensiune exactă, 31 formate reale +Generator de fișiere de test - dimensiune exactă, 32 formate reale @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Un generator gratuit și open source de fișiere de test pentru QA. Produce fișiere reale în 31 formate, la dimensiunea exactă, și scrie un manifest care spune cum trebuie să reacționeze sistemul testat la fiecare.", + "description": "Un generator gratuit și open source de fișiere de test pentru QA. Produce fișiere reale în 32 formate, la dimensiunea exactă, și scrie un manifest care spune cum trebuie să reacționeze sistemul testat la fiecare.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                              Generează fișiere de test reale la dimensiunea exactă

                              - PDF, PNG, DOCX, ZIP - 31 formate în total, și fiecare este un + PDF, PNG, DOCX, ZIP - 32 formate în total, și fiecare este un fișier real care se deschide în programul căruia îi aparține, la exact dimensiunea pe care ai cerut-o. Fiecare rulare notează și ce trebuie să facă aplicația ta cu fiecare fișier. Linie de comandă și fereastră desktop, gratuit și open source, funcționând în întregime @@ -180,7 +180,7 @@

                              Generează fișiere de test reale la dimensiunea exactă

                              • - 31 + 32

                                formate reale, fiecare se deschide în programul căruia îi aparține

                              • @@ -343,7 +343,7 @@

                                Construit pentru o suită care rulează nesupravegheată

                                Cere 10485761 de octeți și primești exact atât. O dimensiune pe care un format nu o poate atinge este o eroare cu un motiv, niciodată un fișier de dimensiune greșită.

                              • -

                                31 formate reale

                                +

                                32 formate reale

                                Nu zerouri de umplutură cu o extensie. Un PNG generat se deschide într-un vizualizator de imagini, un DOCX se deschide în Word, un ZIP se extrage. Fiecare este verificat cu cititori independenți înainte de livrare.

                              • diff --git a/web/public/ro/intrebari-frecvente/index.html b/web/public/ro/intrebari-frecvente/index.html index d0a9dc31..1183e296 100644 --- a/web/public/ro/intrebari-frecvente/index.html +++ b/web/public/ro/intrebari-frecvente/index.html @@ -206,7 +206,7 @@

                                Întrebări frecvente

                                Ce formate urmează?

                                -

                                7z și mp3. Azi funcționează de la un capăt la altul 31 formate.

                                +

                                7z și mp3. Azi funcționează de la un capăt la altul 32 formate.

                                @@ -277,7 +277,7 @@

                                Întrebări frecvente

                                { "@type": "Question", "name": "Ce formate urmează?", - "acceptedAnswer": { "@type": "Answer", "text": "7z și mp3. Azi funcționează de la un capăt la altul 31 formate." } + "acceptedAnswer": { "@type": "Answer", "text": "7z și mp3. Azi funcționează de la un capăt la altul 32 formate." } }, { "@type": "Question", diff --git a/web/public/ro/presetari/empty-and-minimal/index.html b/web/public/ro/presetari/empty-and-minimal/index.html index 40bc4583..8ed28755 100644 --- a/web/public/ro/presetari/empty-and-minimal/index.html +++ b/web/public/ro/presetari/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Cele mai mici fișiere de test valide și goale, în fiecare format - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -179,10 +179,10 @@

                                Ce este în set?

                                - - - - + + + +
                                Fișiere33
                                Targeturi în rețeta lui33
                                Dimensiune totală40 496 B
                                Formateavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                Fișiere34
                                Targeturi în rețeta lui34
                                Dimensiune totală41 762 B
                                Formateavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                @@ -193,7 +193,7 @@

                                Ce este în set?

                                AșteptatSemnificațieFișiere - acceptSistemul tău trebuie să accepte fișierul.31 + acceptSistemul tău trebuie să accepte fișierul.32 unspecifiedDepinde de regulile sistemului tău. Tu decizi, apoi verifici că ce se întâmplă este ce ai vrut.2 diff --git a/web/public/ru/corrupt-test-files/index.html b/web/public/ru/corrupt-test-files/index.html index 460ca1aa..8c76af80 100644 --- a/web/public/ru/corrupt-test-files/index.html +++ b/web/public/ru/corrupt-test-files/index.html @@ -204,7 +204,7 @@

                                Повреждённый файл остаётся нужного разме tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                                Настройки пишутся после двоеточия. Параметр можно повторять, а повреждения применяются в том - порядке, в каком вы их записали. Это работает с каждым из 31 форматов. + порядке, в каком вы их записали. Это работает с каждым из 32 форматов.

                                diff --git a/web/public/ru/faq/index.html b/web/public/ru/faq/index.html index 52903f2c..eefd7955 100644 --- a/web/public/ru/faq/index.html +++ b/web/public/ru/faq/index.html @@ -206,7 +206,7 @@

                                Часто задаваемые вопросы

                                Какие форматы появятся дальше?

                                -

                                7z и mp3. Сегодня полностью работают 31 форматов.

                                +

                                7z и mp3. Сегодня полностью работают 32 форматов.

                                @@ -277,7 +277,7 @@

                                Часто задаваемые вопросы

                                { "@type": "Question", "name": "Какие форматы появятся дальше?", - "acceptedAnswer": { "@type": "Answer", "text": "7z и mp3. Сегодня полностью работают 31 форматов." } + "acceptedAnswer": { "@type": "Answer", "text": "7z и mp3. Сегодня полностью работают 32 форматов." } }, { "@type": "Question", diff --git a/web/public/ru/formats/index.html b/web/public/ru/formats/index.html index ec5d1b5a..a6f79a1d 100644 --- a/web/public/ru/formats/index.html +++ b/web/public/ru/formats/index.html @@ -4,8 +4,8 @@ -31 форматов файлов - PDF, DOCX, PNG, ZIP и другие - +32 форматов файлов - PDF, DOCX, PNG, ZIP и другие + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                                -

                                31 форматов файлов, каждый создаётся точного размера

                                +

                                32 форматов файлов, каждый создаётся точного размера

                                Каждый из них - настоящий файл этого формата. Он открывается в своей программе и имеет ровно то число байт, которое вы запросили. Ни один не является нулями-заполнителями с @@ -284,6 +284,14 @@

                                31 форматов файлов, каждый создаётся точно full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -856,6 +864,51 @@

                                Настройки, которые принимает каждый форм quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + момент в UTC вида 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 записей в секунду + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + истина или ложь + pdf pages diff --git a/web/public/ru/index.html b/web/public/ru/index.html index 92125fc5..bfe576ad 100644 --- a/web/public/ru/index.html +++ b/web/public/ru/index.html @@ -4,7 +4,7 @@ -Генератор тестовых файлов для QA - точный размер, 31 форматов +Генератор тестовых файлов для QA - точный размер, 32 форматов @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Бесплатный генератор тестовых файлов для QA с открытым кодом. Создаёт настоящие файлы в 31 форматах точного размера и записывает манифест с ожидаемой реакцией тестируемой системы на каждый из них.", + "description": "Бесплатный генератор тестовых файлов для QA с открытым кодом. Создаёт настоящие файлы в 32 форматах точного размера и записывает манифест с ожидаемой реакцией тестируемой системы на каждый из них.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                                Создавайте настоящие тестовые файлы точного размера

                                - PDF, PNG, DOCX, ZIP - всего 31 форматов, и каждый из них + PDF, PNG, DOCX, ZIP - всего 32 форматов, и каждый из них настоящий файл, который открывается в своей программе, точно того размера, который вы запросили. Каждый запуск ещё и записывает, что ваше приложение должно делать с каждым файлом. Командная строка и десктопное окно, бесплатно и с открытым кодом, всё работает на вашей @@ -180,7 +180,7 @@

                                Создавайте настоящие тестовые файлы точн
                                • - 31 + 32

                                  настоящих форматов, каждый открывается в своей программе

                                • @@ -343,7 +343,7 @@

                                  Создан для набора тестов, работающего без

                                  Запросите 10485761 байт и получите ровно столько. Размер, которого формат не может достичь, - это ошибка с причиной, а не файл неверного размера.

                                • -

                                  31 настоящих форматов

                                  +

                                  32 настоящих форматов

                                  Не нули-заполнители с расширением. Созданный PNG открывается в просмотрщике изображений, DOCX - в Word, ZIP распаковывается. Каждый формат проверяется независимыми читателями до выпуска.

                                • diff --git a/web/public/ru/presets/empty-and-minimal/index.html b/web/public/ru/presets/empty-and-minimal/index.html index 05fad14f..1047793c 100644 --- a/web/public/ru/presets/empty-and-minimal/index.html +++ b/web/public/ru/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Минимальные корректные и пустые тестовые файлы в каждом формате - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

                                  Что входит в набор?

                                  - - - - + + + +
                                  Файлов33
                                  Целей в его рецепте33
                                  Общий размер40 496 B
                                  Форматыavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                  Файлов34
                                  Целей в его рецепте34
                                  Общий размер41 762 B
                                  Форматыavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                  @@ -192,7 +192,7 @@

                                  Что входит в набор?

                                  ОжидаетсяЗначениеФайлов - acceptВаша система должна принять файл.31 + acceptВаша система должна принять файл.32 unspecifiedЗависит от правил вашей системы. Вы решаете, а затем проверяете, что происходит именно то, что вы имели в виду.2 diff --git a/web/public/social.html b/web/public/social.html index 94e6846a..c0d07149 100644 --- a/web/public/social.html +++ b/web/public/social.html @@ -203,7 +203,7 @@

                                  Test sets,
                                  not just files.

                                  • Presets - ready test sets for size limits, file names, encodings and imports
                                  • Recipes - one YAML file, same bytes in every CI run
                                  • -
                                  • 31 formats with their own settings - pages, pixels, rows, archive contents
                                  • +
                                  • 32 formats with their own settings - pages, pixels, rows, archive contents

                                  Free and open source · works offline · Windows · macOS · Linux

                                diff --git a/web/public/th/corrupt-test-files/index.html b/web/public/th/corrupt-test-files/index.html index f9ebb833..6890b171 100644 --- a/web/public/th/corrupt-test-files/index.html +++ b/web/public/th/corrupt-test-files/index.html @@ -201,7 +201,7 @@

                                ไฟล์ที่เสียหายยังมีขนาดตา tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                                การตั้งค่าเขียนหลังเครื่องหมายทวิภาค ตัวเลือกนี้ใส่ซ้ำได้ - และความเสียหายจะถูกนำมาใช้ตามลำดับที่คุณเขียน ใช้ได้กับทั้ง 31 รูปแบบ + และความเสียหายจะถูกนำมาใช้ตามลำดับที่คุณเขียน ใช้ได้กับทั้ง 32 รูปแบบ

                                diff --git a/web/public/th/faq/index.html b/web/public/th/faq/index.html index cad9dfc6..b6ad57b3 100644 --- a/web/public/th/faq/index.html +++ b/web/public/th/faq/index.html @@ -205,7 +205,7 @@

                                คำถามที่พบบ่อย

                                รูปแบบใดจะมาต่อไป

                                -

                                7z และ mp3 ตอนนี้ 31 รูปแบบทำงานได้ครบตั้งแต่ต้นจนจบ

                                +

                                7z และ mp3 ตอนนี้ 32 รูปแบบทำงานได้ครบตั้งแต่ต้นจนจบ

                                @@ -276,7 +276,7 @@

                                คำถามที่พบบ่อย

                                { "@type": "Question", "name": "รูปแบบใดจะมาต่อไป", - "acceptedAnswer": { "@type": "Answer", "text": "7z และ mp3 ตอนนี้ 31 รูปแบบทำงานได้ครบตั้งแต่ต้นจนจบ" } + "acceptedAnswer": { "@type": "Answer", "text": "7z และ mp3 ตอนนี้ 32 รูปแบบทำงานได้ครบตั้งแต่ต้นจนจบ" } }, { "@type": "Question", diff --git a/web/public/th/formats/index.html b/web/public/th/formats/index.html index 0a5d6835..cefaab3f 100644 --- a/web/public/th/formats/index.html +++ b/web/public/th/formats/index.html @@ -4,8 +4,8 @@ -31 รูปแบบไฟล์ที่รองรับ - PDF, DOCX, PNG, ZIP และอื่นๆ - +32 รูปแบบไฟล์ที่รองรับ - PDF, DOCX, PNG, ZIP และอื่นๆ + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                                -

                                31 รูปแบบไฟล์ แต่ละรูปแบบสร้างที่ขนาดแม่นยำ

                                +

                                32 รูปแบบไฟล์ แต่ละรูปแบบสร้างที่ขนาดแม่นยำ

                                ทุกรูปแบบเป็นไฟล์จริงของรูปแบบนั้น เปิดได้ในซอฟต์แวร์ของมัน และมีจำนวนไบต์ตรงตามที่คุณขอพอดี ไม่มีรูปแบบใดเป็นศูนย์เติมเต็มที่แปะนามสกุลเข้าไป @@ -283,6 +283,14 @@

                                31 รูปแบบไฟล์ แต่ละรูปแบบสร้ full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -854,6 +862,51 @@

                                การตั้งค่าที่แต่ละรูปแบบร quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + เวลาใน UTC เช่น 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 รายการต่อวินาที + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + จริงหรือเท็จ + pdf pages diff --git a/web/public/th/index.html b/web/public/th/index.html index b761d642..c426e697 100644 --- a/web/public/th/index.html +++ b/web/public/th/index.html @@ -4,7 +4,7 @@ -ตัวสร้างไฟล์ทดสอบสำหรับ QA - ขนาดแม่นยำ 31 รูปแบบจริง +ตัวสร้างไฟล์ทดสอบสำหรับ QA - ขนาดแม่นยำ 32 รูปแบบจริง @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "ตัวสร้างไฟล์ทดสอบสำหรับ QA ฟรีและโอเพนซอร์ส สร้างไฟล์จริงใน 31 รูปแบบด้วยขนาดแม่นยำ และเขียนแมนิเฟสต์ที่บอกว่าระบบที่ถูกทดสอบควรตอบสนองต่อแต่ละไฟล์อย่างไร", + "description": "ตัวสร้างไฟล์ทดสอบสำหรับ QA ฟรีและโอเพนซอร์ส สร้างไฟล์จริงใน 32 รูปแบบด้วยขนาดแม่นยำ และเขียนแมนิเฟสต์ที่บอกว่าระบบที่ถูกทดสอบควรตอบสนองต่อแต่ละไฟล์อย่างไร", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                                สร้างไฟล์ทดสอบจริงที่ขนาดแม่นยำ

                                - PDF, PNG, DOCX, ZIP - รวม 31 รูปแบบ + PDF, PNG, DOCX, ZIP - รวม 32 รูปแบบ และทุกไฟล์เป็นไฟล์จริงที่เปิดได้ในซอฟต์แวร์ของมัน ด้วยขนาดตรงตามที่คุณขอพอดี ทุกครั้งที่รันยังจดด้วยว่าแอปพลิเคชันของคุณควรทำอย่างไรกับแต่ละไฟล์ มีทั้งบรรทัดคำสั่งและหน้าต่างเดสก์ท็อป ฟรีและโอเพนซอร์ส ทำงานทั้งหมดบนเครื่องของคุณ @@ -179,7 +179,7 @@

                                สร้างไฟล์ทดสอบจริงที่ขนาด
                                • - 31 + 32

                                  รูปแบบจริง แต่ละรูปแบบเปิดได้ในซอฟต์แวร์ของมัน

                                • @@ -341,7 +341,7 @@

                                  สร้างมาสำหรับชุดทดสอบที่ร

                                  ขอ 10485761 ไบต์ แล้วได้ตามนั้นพอดี ขนาดที่รูปแบบไปไม่ถึงคือข้อผิดพลาดที่มีเหตุผล ไม่ใช่ไฟล์ขนาดผิด

                                • -

                                  31 รูปแบบจริง

                                  +

                                  32 รูปแบบจริง

                                  ไม่ใช่ศูนย์ที่เติมเต็มพร้อมนามสกุล PNG ที่สร้างเปิดได้ในโปรแกรมดูภาพ DOCX เปิดได้ใน Word และ ZIP แตกไฟล์ได้ ทุกรูปแบบถูกตรวจด้วยตัวอ่านอิสระก่อนปล่อย

                                • diff --git a/web/public/th/presets/empty-and-minimal/index.html b/web/public/th/presets/empty-and-minimal/index.html index 917f7f02..56a3dc5b 100644 --- a/web/public/th/presets/empty-and-minimal/index.html +++ b/web/public/th/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ ไฟล์ทดสอบที่ถูกต้องเล็กที่สุดและไฟล์ว่างในทุกรูปแบบ - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

                                  ในชุดมีอะไร

                                  - - - - + + + +
                                  ไฟล์33
                                  target ในสูตรของมัน33
                                  ขนาดรวม40 496 B
                                  รูปแบบavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                  ไฟล์34
                                  target ในสูตรของมัน34
                                  ขนาดรวม41 762 B
                                  รูปแบบavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                  @@ -192,7 +192,7 @@

                                  ในชุดมีอะไร

                                  ที่คาดหวังความหมายไฟล์ - acceptระบบของคุณควรยอมรับไฟล์นี้31 + acceptระบบของคุณควรยอมรับไฟล์นี้32 unspecifiedขึ้นอยู่กับกฎของระบบของคุณ คุณเป็นผู้ตัดสิน แล้วตรวจสอบว่าสิ่งที่เกิดขึ้นตรงกับที่ตั้งใจ2 diff --git a/web/public/tr/bicimler/index.html b/web/public/tr/bicimler/index.html index 652ec92a..c4505883 100644 --- a/web/public/tr/bicimler/index.html +++ b/web/public/tr/bicimler/index.html @@ -4,8 +4,8 @@ -31 desteklenen dosya biçimi - PDF, DOCX, PNG, ZIP ve daha fazlası - +32 desteklenen dosya biçimi - PDF, DOCX, PNG, ZIP ve daha fazlası + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                                  -

                                  31 dosya biçimi, her biri tam boyutta üretilir

                                  +

                                  32 dosya biçimi, her biri tam boyutta üretilir

                                  Bunların her biri o biçimde gerçek bir dosyadır. Ait olduğu programda açılır ve tam istediğiniz bayt sayısındadır. Hiçbiri yapıştırılmış uzantılı dolgu sıfırları değildir. @@ -283,6 +283,14 @@

                                  31 dosya biçimi, her biri tam boyutta üretilir

                                  full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -856,6 +864,51 @@

                                  Her biçimin kabul ettiği ayarlar

                                  quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + 2026-08-01T12:00:00Z gibi UTC'de bir an + + + + rate + 1 - 1000000 saniyedeki girdi + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + doğru veya yanlış + pdf pages diff --git a/web/public/tr/bozuk-test-dosyalari/index.html b/web/public/tr/bozuk-test-dosyalari/index.html index 207cee25..7024df1f 100644 --- a/web/public/tr/bozuk-test-dosyalari/index.html +++ b/web/public/tr/bozuk-test-dosyalari/index.html @@ -203,7 +203,7 @@

                                  Hasarlı bir dosya yine istediğiniz boyuttadır

                                  tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                                  Ayarlar iki noktadan sonra yazılır. Seçenek tekrarlanabilir ve hasarlar yazdığınız sırayla - uygulanır. 31 biçimin her biriyle çalışır. + uygulanır. 32 biçimin her biriyle çalışır.

                                  diff --git a/web/public/tr/hazir-ayarlar/empty-and-minimal/index.html b/web/public/tr/hazir-ayarlar/empty-and-minimal/index.html index 490188b4..d4242cf1 100644 --- a/web/public/tr/hazir-ayarlar/empty-and-minimal/index.html +++ b/web/public/tr/hazir-ayarlar/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Her biçimde en küçük geçerli ve boş test dosyaları - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -179,10 +179,10 @@

                                  Sette ne var?

                                  - - - - + + + +
                                  Dosya33
                                  Tarifindeki hedefler33
                                  Toplam boyut40 496 B
                                  Biçimleravif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                  Dosya34
                                  Tarifindeki hedefler34
                                  Toplam boyut41 762 B
                                  Biçimleravif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                  @@ -193,7 +193,7 @@

                                  Sette ne var?

                                  BeklenenAnlamıDosya - acceptSisteminiz dosyayı kabul etmeli.31 + acceptSisteminiz dosyayı kabul etmeli.32 unspecifiedSisteminizin kurallarına bağlı. Siz karar verirsiniz, sonra olanın amaçladığınız şey olduğunu doğrularsınız.2 diff --git a/web/public/tr/index.html b/web/public/tr/index.html index 59090a8c..801bd92d 100644 --- a/web/public/tr/index.html +++ b/web/public/tr/index.html @@ -4,7 +4,7 @@ -QA için test dosyası oluşturucu - tam boyut, 31 gerçek biçim +QA için test dosyası oluşturucu - tam boyut, 32 gerçek biçim @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "QA için ücretsiz ve açık kaynaklı bir test dosyası oluşturucu. 31 biçimde tam boyutta gerçek dosyalar üretir ve test edilen sistemin her birine nasıl tepki vermesi gerektiğini söyleyen bir manifest yazar.", + "description": "QA için ücretsiz ve açık kaynaklı bir test dosyası oluşturucu. 32 biçimde tam boyutta gerçek dosyalar üretir ve test edilen sistemin her birine nasıl tepki vermesi gerektiğini söyleyen bir manifest yazar.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                                  Tam boyutta gerçek test dosyaları üretin

                                  - PDF, PNG, DOCX, ZIP - toplam 31 biçim ve her biri ait olduğu + PDF, PNG, DOCX, ZIP - toplam 32 biçim ve her biri ait olduğu programda açılan, tam istediğiniz boyutta gerçek bir dosya. Her çalıştırma ayrıca uygulamanızın her dosyayla ne yapması gerektiğini de yazar. Komut satırı ve masaüstü penceresi, ücretsiz ve açık kaynak, tamamen makinenizde çalışır. @@ -179,7 +179,7 @@

                                  Tam boyutta gerçek test dosyaları üretin

                                  • - 31 + 32

                                    gerçek biçim, her biri ait olduğu programda açılır

                                  • @@ -342,7 +342,7 @@

                                    Gözetimsiz çalışan bir test takımı için yapıldı

                                    10485761 bayt isteyin ve tam olarak onu alın. Bir biçimin ulaşamayacağı boyut nedeni olan bir hatadır, asla yanlış boyutta bir dosya değil.

                                  • -

                                    31 gerçek biçim

                                    +

                                    32 gerçek biçim

                                    Uzantılı dolgu sıfırları değil. Üretilmiş bir PNG resim görüntüleyicide, bir DOCX Word'de açılır, bir ZIP çıkarılır. Her biri yayımlanmadan önce bağımsız okuyucularla doğrulanır.

                                  • diff --git a/web/public/tr/sss/index.html b/web/public/tr/sss/index.html index ce599e39..86f87d6a 100644 --- a/web/public/tr/sss/index.html +++ b/web/public/tr/sss/index.html @@ -205,7 +205,7 @@

                                    Sık sorulan sorular

                                    Sırada hangi biçimler var?

                                    -

                                    7z ve mp3. Bugün 31 biçim uçtan uca çalışıyor.

                                    +

                                    7z ve mp3. Bugün 32 biçim uçtan uca çalışıyor.

                                    @@ -276,7 +276,7 @@

                                    Sık sorulan sorular

                                    { "@type": "Question", "name": "Sırada hangi biçimler var?", - "acceptedAnswer": { "@type": "Answer", "text": "7z ve mp3. Bugün 31 biçim uçtan uca çalışıyor." } + "acceptedAnswer": { "@type": "Answer", "text": "7z ve mp3. Bugün 32 biçim uçtan uca çalışıyor." } }, { "@type": "Question", diff --git a/web/public/uk/corrupt-test-files/index.html b/web/public/uk/corrupt-test-files/index.html index 71a15cdc..7d041a57 100644 --- a/web/public/uk/corrupt-test-files/index.html +++ b/web/public/uk/corrupt-test-files/index.html @@ -204,7 +204,7 @@

                                    Пошкоджений файл лишається потрібного ро tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                                    Налаштування пишуться після двокрапки. Параметр можна повторювати, а пошкодження застосовуються в - тому порядку, в якому ви їх записали. Це працює з кожним із 31 форматів. + тому порядку, в якому ви їх записали. Це працює з кожним із 32 форматів.

                                    diff --git a/web/public/uk/faq/index.html b/web/public/uk/faq/index.html index 25e8c726..65cc65ff 100644 --- a/web/public/uk/faq/index.html +++ b/web/public/uk/faq/index.html @@ -206,7 +206,7 @@

                                    Часті запитання

                                    Які формати з'являться далі?

                                    -

                                    7z і mp3. Сьогодні повністю працюють 31 форматів.

                                    +

                                    7z і mp3. Сьогодні повністю працюють 32 форматів.

                                    @@ -277,7 +277,7 @@

                                    Часті запитання

                                    { "@type": "Question", "name": "Які формати з\u0027являться далі?", - "acceptedAnswer": { "@type": "Answer", "text": "7z і mp3. Сьогодні повністю працюють 31 форматів." } + "acceptedAnswer": { "@type": "Answer", "text": "7z і mp3. Сьогодні повністю працюють 32 форматів." } }, { "@type": "Question", diff --git a/web/public/uk/formats/index.html b/web/public/uk/formats/index.html index da116462..5b0c857e 100644 --- a/web/public/uk/formats/index.html +++ b/web/public/uk/formats/index.html @@ -4,8 +4,8 @@ -31 форматів файлів - PDF, DOCX, PNG, ZIP та інші - +32 форматів файлів - PDF, DOCX, PNG, ZIP та інші + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                                    -

                                    31 форматів файлів, кожен створюється точного розміру

                                    +

                                    32 форматів файлів, кожен створюється точного розміру

                                    Кожен із них - справжній файл цього формату. Він відкривається у своїй програмі та має рівно стільки байтів, скільки ви запросили. Жоден не є нулями-заповнювачами з приклеєним @@ -284,6 +284,14 @@

                                    31 форматів файлів, кожен створюється точн full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -856,6 +864,51 @@

                                    Налаштування, які приймає кожен формат

                                    quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + момент в UTC на кшталт 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 записів на секунду + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + істина або хибність + pdf pages diff --git a/web/public/uk/index.html b/web/public/uk/index.html index 592a56c8..32a85583 100644 --- a/web/public/uk/index.html +++ b/web/public/uk/index.html @@ -4,7 +4,7 @@ -Генератор тестових файлів для QA - точний розмір, 31 форматів +Генератор тестових файлів для QA - точний розмір, 32 форматів @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Безкоштовний генератор тестових файлів для QA з відкритим кодом. Створює справжні файли у 31 форматах точного розміру й записує маніфест з очікуваною реакцією тестованої системи на кожен із них.", + "description": "Безкоштовний генератор тестових файлів для QA з відкритим кодом. Створює справжні файли у 32 форматах точного розміру й записує маніфест з очікуваною реакцією тестованої системи на кожен із них.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                                    Створюйте справжні тестові файли точного розміру

                                    - PDF, PNG, DOCX, ZIP - усього 31 форматів, і кожен із них - + PDF, PNG, DOCX, ZIP - усього 32 форматів, і кожен із них - справжній файл, що відкривається у своїй програмі, точно того розміру, який ви запросили. Кожен запуск ще й записує, що ваш застосунок має робити з кожним файлом. Командний рядок і десктопне вікно, безкоштовно та з відкритим кодом, усе працює на вашій машині. @@ -179,7 +179,7 @@

                                    Створюйте справжні тестові файли точного
                                    • - 31 + 32

                                      справжніх форматів, кожен відкривається у своїй програмі

                                    • @@ -342,7 +342,7 @@

                                      Створений для набору тестів, що працює бе

                                      Запросіть 10485761 байт і отримайте рівно стільки. Розмір, якого формат не може досягти, - це помилка з причиною, а не файл неправильного розміру.

                                    • -

                                      31 справжніх форматів

                                      +

                                      32 справжніх форматів

                                      Не нулі-заповнювачі з розширенням. Створений PNG відкривається в переглядачі зображень, DOCX - у Word, ZIP розпаковується. Кожен формат перевіряється незалежними читачами до випуску.

                                    • diff --git a/web/public/uk/presets/empty-and-minimal/index.html b/web/public/uk/presets/empty-and-minimal/index.html index 54d4b655..c74e75ce 100644 --- a/web/public/uk/presets/empty-and-minimal/index.html +++ b/web/public/uk/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Мінімальні коректні та порожні тестові файли в кожному форматі - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

                                      Що входить у набір?

                                      - - - - + + + +
                                      Файлів33
                                      Цілей у його рецепті33
                                      Загальний розмір40 496 B
                                      Форматиavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                      Файлів34
                                      Цілей у його рецепті34
                                      Загальний розмір41 762 B
                                      Форматиavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                      @@ -192,7 +192,7 @@

                                      Що входить у набір?

                                      ОчікуєтьсяЗначенняФайлів - acceptВаша система має прийняти файл.31 + acceptВаша система має прийняти файл.32 unspecifiedЗалежить від правил вашої системи. Ви вирішуєте, а потім перевіряєте, що відбувається саме те, що ви мали на увазі.2 diff --git a/web/public/vi/dinh-dang/index.html b/web/public/vi/dinh-dang/index.html index 3766772e..5f1fb533 100644 --- a/web/public/vi/dinh-dang/index.html +++ b/web/public/vi/dinh-dang/index.html @@ -4,8 +4,8 @@ -31 định dạng tệp được hỗ trợ - PDF, DOCX, PNG, ZIP và hơn nữa - +32 định dạng tệp được hỗ trợ - PDF, DOCX, PNG, ZIP và hơn nữa + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                                      -

                                      31 định dạng tệp, mỗi định dạng được tạo ở kích thước chính xác

                                      +

                                      32 định dạng tệp, mỗi định dạng được tạo ở kích thước chính xác

                                      Mỗi cái là một tệp thật của định dạng đó. Nó mở được bằng phần mềm của nó và có đúng số byte bạn đã yêu cầu. Không cái nào là số không độn với phần mở rộng dán vào. @@ -283,6 +283,14 @@

                                      31 định dạng tệp, mỗi định dạng được tạo ở kích thư full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -854,6 +862,51 @@

                                      Các thiết lập mỗi định dạng nhận

                                      quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + thời điểm theo UTC như 2026-08-01T12:00:00Z + + + + rate + 1 - 1000000 mục mỗi giây + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + đúng hoặc sai + pdf pages diff --git a/web/public/vi/faq/index.html b/web/public/vi/faq/index.html index aa4d7310..d6175e85 100644 --- a/web/public/vi/faq/index.html +++ b/web/public/vi/faq/index.html @@ -206,7 +206,7 @@

                                      Câu hỏi thường gặp

                                      Những định dạng nào sắp có?

                                      -

                                      7z và mp3. Hôm nay có 31 định dạng hoạt động trọn vẹn từ đầu đến cuối.

                                      +

                                      7z và mp3. Hôm nay có 32 định dạng hoạt động trọn vẹn từ đầu đến cuối.

                                      @@ -277,7 +277,7 @@

                                      Câu hỏi thường gặp

                                      { "@type": "Question", "name": "Những định dạng nào sắp có?", - "acceptedAnswer": { "@type": "Answer", "text": "7z và mp3. Hôm nay có 31 định dạng hoạt động trọn vẹn từ đầu đến cuối." } + "acceptedAnswer": { "@type": "Answer", "text": "7z và mp3. Hôm nay có 32 định dạng hoạt động trọn vẹn từ đầu đến cuối." } }, { "@type": "Question", diff --git a/web/public/vi/index.html b/web/public/vi/index.html index 4533eeba..0e83d855 100644 --- a/web/public/vi/index.html +++ b/web/public/vi/index.html @@ -4,7 +4,7 @@ -Trình tạo tệp kiểm thử cho QA - kích thước chính xác, 31 định dạng +Trình tạo tệp kiểm thử cho QA - kích thước chính xác, 32 định dạng @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "Trình tạo tệp kiểm thử miễn phí, mã nguồn mở cho QA. Nó tạo tệp thật ở 31 định dạng, đúng kích thước, và ghi manifest nêu cách hệ thống được kiểm thử cần phản ứng với từng tệp.", + "description": "Trình tạo tệp kiểm thử miễn phí, mã nguồn mở cho QA. Nó tạo tệp thật ở 32 định dạng, đúng kích thước, và ghi manifest nêu cách hệ thống được kiểm thử cần phản ứng với từng tệp.", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                                      Tạo tệp kiểm thử thật đúng kích thước

                                      - PDF, PNG, DOCX, ZIP - tổng cộng 31 định dạng, và mỗi tệp là + PDF, PNG, DOCX, ZIP - tổng cộng 32 định dạng, và mỗi tệp là tệp thật mở được bằng phần mềm của nó, ở đúng kích thước bạn yêu cầu. Mỗi lần chạy còn ghi lại ứng dụng của bạn cần làm gì với từng tệp. Dòng lệnh và cửa sổ desktop, miễn phí, mã nguồn mở, chạy hoàn toàn trên máy của bạn. @@ -179,7 +179,7 @@

                                      Tạo tệp kiểm thử thật đúng kích thước

                                      • - 31 + 32

                                        định dạng thật, mỗi định dạng mở được bằng phần mềm của nó

                                      • @@ -341,7 +341,7 @@

                                        Làm cho bộ kiểm thử chạy không cần người trông

                                        Yêu cầu 10485761 byte và nhận đúng như vậy. Kích thước mà định dạng không thể đạt là một lỗi có lý do, không bao giờ là tệp sai kích thước.

                                      • -

                                        31 định dạng thật

                                        +

                                        32 định dạng thật

                                        Không phải số không độn kèm một phần mở rộng. PNG được tạo mở trong trình xem ảnh, DOCX mở trong Word, ZIP giải nén được. Mỗi định dạng được kiểm tra bằng các trình đọc độc lập trước khi phát hành.

                                      • diff --git a/web/public/vi/preset/empty-and-minimal/index.html b/web/public/vi/preset/empty-and-minimal/index.html index bd18ca78..df526ae0 100644 --- a/web/public/vi/preset/empty-and-minimal/index.html +++ b/web/public/vi/preset/empty-and-minimal/index.html @@ -5,7 +5,7 @@ Tệp kiểm thử hợp lệ nhỏ nhất và tệp rỗng ở mọi định dạng - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -178,10 +178,10 @@

                                        Trong bộ có gì?

                                        - - - - + + + +
                                        Tệp33
                                        Target trong công thức của nó33
                                        Tổng kích thước40 496 B
                                        Định dạngavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                        Tệp34
                                        Target trong công thức của nó34
                                        Tổng kích thước41 762 B
                                        Định dạngavif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                        @@ -192,7 +192,7 @@

                                        Trong bộ có gì?

                                        Mong đợiÝ nghĩaTệp - acceptHệ thống của bạn nên nhận tệp.31 + acceptHệ thống của bạn nên nhận tệp.32 unspecifiedTùy vào quy tắc của hệ thống bạn. Bạn quyết định, rồi kiểm tra điều xảy ra có đúng ý bạn không.2 diff --git a/web/public/vi/tep-kiem-thu-bi-hong/index.html b/web/public/vi/tep-kiem-thu-bi-hong/index.html index f9fb5d03..94c6a6e5 100644 --- a/web/public/vi/tep-kiem-thu-bi-hong/index.html +++ b/web/public/vi/tep-kiem-thu-bi-hong/index.html @@ -201,7 +201,7 @@

                                        Tệp bị hỏng vẫn có kích thước bạn đã yêu cầu

                                        tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                                        Cài đặt viết sau dấu hai chấm. Tùy chọn có thể lặp lại, và các hỏng hóc được áp dụng theo thứ tự bạn - viết. Nó dùng được với từng định dạng trong 31 định dạng. + viết. Nó dùng được với từng định dạng trong 32 định dạng.

                                        diff --git a/web/public/zh-hans/corrupt-test-files/index.html b/web/public/zh-hans/corrupt-test-files/index.html index 9d1ec75a..e405ac18 100644 --- a/web/public/zh-hans/corrupt-test-files/index.html +++ b/web/public/zh-hans/corrupt-test-files/index.html @@ -191,7 +191,7 @@

                                        损坏的文件仍然是你要求的大小

                                        tfg generate --format zip --size 5mb --damage truncate:missing=30 --out ./cut tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                                        - 设置写在冒号之后。该选项可以重复,损坏按你写下的顺序依次应用。它适用于全部 31 种格式。 + 设置写在冒号之后。该选项可以重复,损坏按你写下的顺序依次应用。它适用于全部 32 种格式。

                                        diff --git a/web/public/zh-hans/faq/index.html b/web/public/zh-hans/faq/index.html index c7a12842..0f77e0be 100644 --- a/web/public/zh-hans/faq/index.html +++ b/web/public/zh-hans/faq/index.html @@ -204,7 +204,7 @@

                                        常见问题

                                        接下来会支持哪些格式?

                                        -

                                        7z 和 mp3。目前有 31 种格式可以端到端使用。

                                        +

                                        7z 和 mp3。目前有 32 种格式可以端到端使用。

                                        @@ -275,7 +275,7 @@

                                        常见问题

                                        { "@type": "Question", "name": "接下来会支持哪些格式?", - "acceptedAnswer": { "@type": "Answer", "text": "7z 和 mp3。目前有 31 种格式可以端到端使用。" } + "acceptedAnswer": { "@type": "Answer", "text": "7z 和 mp3。目前有 32 种格式可以端到端使用。" } }, { "@type": "Question", diff --git a/web/public/zh-hans/formats/index.html b/web/public/zh-hans/formats/index.html index f15c26d0..713aae95 100644 --- a/web/public/zh-hans/formats/index.html +++ b/web/public/zh-hans/formats/index.html @@ -4,8 +4,8 @@ -31 种支持的文件格式 - PDF、DOCX、PNG、ZIP 等 - +32 种支持的文件格式 - PDF、DOCX、PNG、ZIP 等 + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                                        -

                                        31 种文件格式,每一种都按精确大小生成

                                        +

                                        32 种文件格式,每一种都按精确大小生成

                                        它们每一个都是该格式的真实文件。它能在对应软件中打开,字节数恰好等于你的要求。没有一个是粘上扩展名的填充零。

                                        @@ -282,6 +282,14 @@

                                        31 种文件格式,每一种都按精确大小生成

                                        full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -848,6 +856,51 @@

                                        每种格式接受的设置

                                        quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + 形如 2026-08-01T12:00:00Z 的 UTC 时刻 + + + + rate + 1 - 1000000 条目每秒 + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + 真或假 + pdf pages diff --git a/web/public/zh-hans/index.html b/web/public/zh-hans/index.html index 3ba8af4c..f60ca4ca 100644 --- a/web/public/zh-hans/index.html +++ b/web/public/zh-hans/index.html @@ -4,7 +4,7 @@ -测试文件生成器 - 精确大小,31 种真实格式 +测试文件生成器 - 精确大小,32 种真实格式 @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "面向 QA 的免费开源测试文件生成器。它生成 31 种格式、大小精确的真实文件,并写出说明被测系统应如何响应每个文件的清单。", + "description": "面向 QA 的免费开源测试文件生成器。它生成 32 种格式、大小精确的真实文件,并写出说明被测系统应如何响应每个文件的清单。", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                                        生成大小精确的真实测试文件

                                        - PDF、PNG、DOCX、ZIP,共 31 + PDF、PNG、DOCX、ZIP,共 32 种格式,每一种都是能在对应软件中打开的真实文件,大小恰好等于你的要求。每次运行还会写下你的应用应该如何处理每个文件。命令行加桌面窗口,免费开源,完全在你的机器上运行。

                                        @@ -177,7 +177,7 @@

                                        生成大小精确的真实测试文件

                                        • - 31 + 32

                                          种真实格式,每一种都能在对应软件中打开

                                        • @@ -332,7 +332,7 @@

                                          为无人值守的测试套件而生

                                          请求 10485761 字节,就得到恰好这么多。格式无法达到的大小会得到带原因的错误,绝不会得到大小错误的文件。

                                        • -

                                          31 种真实格式

                                          +

                                          32 种真实格式

                                          不是带扩展名的填充零。生成的 PNG 能在图片查看器中打开,DOCX 能在 Word 中打开,ZIP 能解压。每种格式在发布前都经过独立读取器验证。

                                        • diff --git a/web/public/zh-hans/presets/empty-and-minimal/index.html b/web/public/zh-hans/presets/empty-and-minimal/index.html index 8e8e7a15..8cc1b357 100644 --- a/web/public/zh-hans/presets/empty-and-minimal/index.html +++ b/web/public/zh-hans/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ 各格式最小的合法文件与空文件 - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -177,10 +177,10 @@

                                          集合里有什么?

                                          - - - - + + + +
                                          文件数33
                                          其配方中的 target 数33
                                          总大小40 496 B
                                          格式avif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                          文件数34
                                          其配方中的 target 数34
                                          总大小41 762 B
                                          格式avif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                          @@ -191,7 +191,7 @@

                                          集合里有什么?

                                          预期含义文件数 - accept你的系统应该接受这个文件。31 + accept你的系统应该接受这个文件。32 unspecified取决于你的系统规则。由你决定,然后检查实际发生的是否符合你的本意。2 diff --git a/web/public/zh-hant/corrupt-test-files/index.html b/web/public/zh-hant/corrupt-test-files/index.html index 401e769c..ec6268e0 100644 --- a/web/public/zh-hant/corrupt-test-files/index.html +++ b/web/public/zh-hant/corrupt-test-files/index.html @@ -191,7 +191,7 @@

                                          損毀的檔案仍然是你要求的大小

                                          tfg generate --format zip --size 5mb --damage truncate:missing=30 --out ./cut tfg generate --format jpg --size 500kb --damage append-junk --out ./tail

                                          - 設定寫在冒號之後。該選項可以重複,損毀依你寫下的順序逐一套用。它適用於全部 31 種格式。 + 設定寫在冒號之後。該選項可以重複,損毀依你寫下的順序逐一套用。它適用於全部 32 種格式。

                                          diff --git a/web/public/zh-hant/faq/index.html b/web/public/zh-hant/faq/index.html index 9e508c5d..3fe57f94 100644 --- a/web/public/zh-hant/faq/index.html +++ b/web/public/zh-hant/faq/index.html @@ -204,7 +204,7 @@

                                          常見問題

                                          接下來會支援哪些格式?

                                          -

                                          7z 與 mp3。目前有 31 種格式可以端到端使用。

                                          +

                                          7z 與 mp3。目前有 32 種格式可以端到端使用。

                                          @@ -275,7 +275,7 @@

                                          常見問題

                                          { "@type": "Question", "name": "接下來會支援哪些格式?", - "acceptedAnswer": { "@type": "Answer", "text": "7z 與 mp3。目前有 31 種格式可以端到端使用。" } + "acceptedAnswer": { "@type": "Answer", "text": "7z 與 mp3。目前有 32 種格式可以端到端使用。" } }, { "@type": "Question", diff --git a/web/public/zh-hant/formats/index.html b/web/public/zh-hant/formats/index.html index d1ead359..37f5e3e8 100644 --- a/web/public/zh-hant/formats/index.html +++ b/web/public/zh-hant/formats/index.html @@ -4,8 +4,8 @@ -31 種支援的檔案格式 - PDF、DOCX、PNG、ZIP 等 - +32 種支援的檔案格式 - PDF、DOCX、PNG、ZIP 等 + @@ -33,8 +33,8 @@ - - + + @@ -62,8 +62,8 @@ - - + + @@ -144,7 +144,7 @@
                                          -

                                          31 種檔案格式,每一種都以精確大小產生

                                          +

                                          32 種檔案格式,每一種都以精確大小產生

                                          它們每一個都是該格式的真實檔案。它能在對應軟體中開啟,位元組數恰好等於你的要求。沒有一個是黏上副檔名的填充零。

                                          @@ -282,6 +282,14 @@

                                          31 種檔案格式,每一種都以精確大小產生

                                          full libaom + + otlp + OpenTelemetry Protocol JSON + .jsonl + 1266 + full + otelcol + pdf Portable Document Format @@ -848,6 +856,51 @@

                                          每種格式接受的設定

                                          quality 1 - 100 + + otlp + signal + logs, traces + + + + formatting + indented, minified, record-per-line + + + + batch + 1 - 1000 + + + + start + 形如 2026-08-01T12:00:00Z 的 UTC 時刻 + + + + rate + 1 - 1000000 每秒項目數 + + + + depth + 1 - 100 + + + + status_mix + client-errors, realistic, server-errors, success + + + + level_mix + debug, errors, quiet, realistic + + + + trace_context + 真或假 + pdf pages diff --git a/web/public/zh-hant/index.html b/web/public/zh-hant/index.html index ae81d448..4ad5e4f2 100644 --- a/web/public/zh-hant/index.html +++ b/web/public/zh-hant/index.html @@ -4,7 +4,7 @@ -測試檔案產生器 - 精確大小,31 種真實格式 +測試檔案產生器 - 精確大小,32 種真實格式 @@ -33,7 +33,7 @@ - + @@ -62,7 +62,7 @@ - + @@ -76,7 +76,7 @@ "@type": "SoftwareApplication", "name": "Testing Files Generator", "alternateName": "tfg", - "description": "專為 QA 設計的免費開源測試檔案產生器。它產生 31 種格式、大小精確的真實檔案,並寫出說明受測系統應如何回應每個檔案的清單。", + "description": "專為 QA 設計的免費開源測試檔案產生器。它產生 32 種格式、大小精確的真實檔案,並寫出說明受測系統應如何回應每個檔案的清單。", "url": "https:\/\/testingfilesgenerator.donislawdev.com/", "applicationCategory": "DeveloperApplication", "applicationSubCategory": "Software Testing", @@ -157,7 +157,7 @@

                                          產生大小精確的真實測試檔案

                                          - PDF、PNG、DOCX、ZIP,共 31 + PDF、PNG、DOCX、ZIP,共 32 種格式,每一種都是能在對應軟體中開啟的真實檔案,大小恰好等於你的要求。每次執行還會寫下你的應用程式應該如何處理每個檔案。命令列加桌面視窗,免費開源,完全在你的電腦上執行。

                                          @@ -177,7 +177,7 @@

                                          產生大小精確的真實測試檔案

                                          • - 31 + 32

                                            種真實格式,每一種都能在對應軟體中開啟

                                          • @@ -332,7 +332,7 @@

                                            為無人看管的測試套件而生

                                            要求 10485761 位元組,就得到恰好這麼多。格式無法達到的大小會得到附上原因的錯誤,絕不會得到大小錯誤的檔案。

                                          • -

                                            31 種真實格式

                                            +

                                            32 種真實格式

                                            不是帶副檔名的填充零。產生的 PNG 能在圖片檢視器中開啟,DOCX 能在 Word 中開啟,ZIP 能解壓縮。每種格式在發布前都經過獨立讀取器驗證。

                                          • diff --git a/web/public/zh-hant/presets/empty-and-minimal/index.html b/web/public/zh-hant/presets/empty-and-minimal/index.html index 8c93885f..597e8ffa 100644 --- a/web/public/zh-hant/presets/empty-and-minimal/index.html +++ b/web/public/zh-hant/presets/empty-and-minimal/index.html @@ -5,7 +5,7 @@ 各格式最小的合法檔案與空檔案 - + @@ -34,7 +34,7 @@ - + @@ -63,7 +63,7 @@ - + @@ -177,10 +177,10 @@

                                            組合裡有什麼?

                                            - - - - + + + +
                                            檔案數33
                                            其配方中的 target 數33
                                            總大小40 496 B
                                            格式avif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                            檔案數34
                                            其配方中的 target 數34
                                            總大小41 762 B
                                            格式avif, bmp, csv, docx, eml, geojson, gif, html, ico, jpg, json, jxl, log, md, mp4, otlp, pdf, png, pptx, rtf, svg, targz, tiff, toml, txt, wav, webm, webp, xlsx, xml, yaml, zip
                                            @@ -191,7 +191,7 @@

                                            組合裡有什麼?

                                            預期意義檔案數 - accept你的系統應該接受這個檔案。31 + accept你的系統應該接受這個檔案。32 unspecified取決於你的系統規則。由你決定,然後檢查實際發生的是否符合你的本意。2 diff --git a/web/social-preview.sha256 b/web/social-preview.sha256 index d48cc131..958912db 100644 --- a/web/social-preview.sha256 +++ b/web/social-preview.sha256 @@ -1 +1 @@ -5b18d0390a6e9f32c54a3cff95460e702253aa438be1574f302ffae348498549 +44e8cdf608b1b5fc20836da0bfb126e4b6205cb4fce2be104772741ca2e8353e