mix bash_fixtures.gen (JustBash v0.4.0)

View Source

Generates enumerated fixture matrices — the cases nobody thinks to write.

The corpus grew by example: someone fixed a bug, then wrote a case for the shape they had just fixed. That leaves whole alphabets unexamined. date.json has 30 hand-written cases and covers none of %F, %D, %T, %R, -I or -v — the exact directives that shipped emitting themselves literally at exit 0, which an agent then used as if it were a date.

A command's flag and directive sets are finite. They are not a space to sample with a fuzzer; they are a list to enumerate. This task writes that enumeration out as ordinary case files, so recording and comparison work exactly as they do for hand-written cases:

mix bash_fixtures.gen date       # write the matrix
mix bash_fixtures date_matrix    # record what real bash does
mix test --only suite:date_matrix

mix bash_fixtures.gen printf     # write the matrix
mix bash_fixtures printf_matrix  # record what real bash does
mix test --only suite:printf_matrix

mix bash_fixtures.gen test       # write the matrix
mix bash_fixtures test_matrix    # record what real bash does
mix test --only suite:test_matrix

mix bash_fixtures.gen varop      # write the matrix
mix bash_fixtures varop_matrix   # record what real bash does
mix test --only suite:varop_matrix

mix bash_fixtures.gen flags      # write the matrix
mix bash_fixtures flags_matrix   # record what real bash does
mix test --only suite:flags_matrix

mix bash_fixtures.gen seto       # write the matrix
mix bash_fixtures seto_matrix    # record what real bash does
mix test --only suite:seto_matrix

Generated suites are named <matrix>_matrix and are safe to regenerate: the digest in JustBash.Fixtures is content-derived, so a case that did not change keeps its recording.

Matrices

  • date — every strftime conversion alone, adjacently paired, and crossed with every field flag, locale modifier, width and compound form, plus -I granularities, -d input forms, and flags GNU date does not have
  • printf — every bash/coreutils conversion (%s %c %d %i %u %o %x %X %f %e %E %g %G %a %A %%, plus bash extras %b %q) alone at two or more bases, then crossed with every format flag, width, precision and *, plus %b/%q inputs that make escapes and quoting visible, and format recycling with excess arguments. Oils builtin-printf.test.sh is a cross-check, not this matrix. See #70 item 2.
  • test — every POSIX/[ operator (-b -c -d -e -f -g -h -k -L -n -p -r -S -s -t -u -w -x -z, = == != < >, -eq -ne -lt -le -gt -ge, -nt -ot -ef, -a -o, !, plus bash extras -G -O -N) crossed with revealing operand shapes for that operator — file/dir/missing/empty/ symlink/dangling for file tests, empty/nonempty/"0" for -z/-n, equal/ unequal/10-vs-9 for string and integer compares. Both test and [ for a representative subset so they cannot drift; the full operator list is on test. This is not the item-3 filesystem-shape × command cube. See #70 item 2.
  • varop — every POSIX/bash ${var op word} form (- :- = := + :+ ? :?, # ## % %%, ${#var}, ${var:offset}, ${var:offset:len}, ${var/pat/rep}, ${var//pat/rep}) crossed with unset / set-empty / set-nonempty, and with a nonempty vs empty word where that changes the answer. Two nonempty bases (foo vs foobar, plus foofoo where shortest vs longest or first vs all would otherwise hide) so one value cannot hide the bug. Suite varop_matrix. See #70 item 2.
  • flags — cmd --jb-not-a-flag and cmd -Z for every name in Commands.Registry. The default assertion is non-zero exit and usage-bearing stderr (never exit 0). Commands that legitimately treat the token as an operand (echo), accept GNU -Z (SELinux, diff -Z, grep -Z, curl -Z), or have no GNU twin (markdown/md) are still generated and named with a reason — never silently omitted. Suite flags_matrix. This is the registry-wide unknown-flag probe from #70 item 2; unknown_flags_test.exs keeps the FlagParser unit tests from #68 rather than a second classification table.
  • seto — every POSIX set -o name and every extra bash name set -o lists, plus unknown names, each asked as -o and +o. Names JustBash.Commands.Set implements are generated unmarked; real names this shell rejects, and names bash itself refuses, are still generated and named with a reason — never silently omitted. Suite seto_matrix. This is the set -o name alphabet from #70 item 2, not the short-option cluster (-euo) or set -o with no name (that lists every option).

A list of conversions and a list of flags are each easy to write down. The cross of the two is where the bugs live and is what nobody enumerates by hand: the first version of this matrix listed both alphabets and still missed that %-T rendered "9:05:03", because it never asked a flag and a conversion in the same breath. Cross what you enumerate.

Usage

mix bash_fixtures.gen              # every matrix
mix bash_fixtures.gen date         # one matrix
mix bash_fixtures.gen printf       # one matrix
mix bash_fixtures.gen test         # one matrix
mix bash_fixtures.gen varop        # one matrix
mix bash_fixtures.gen flags        # one matrix
mix bash_fixtures.gen seto         # one matrix
mix bash_fixtures.gen --dry-run    # report counts, write nothing