F3.2: assert Diag for the zero-arg and too-many-flags raise sites
Example 0717 now asserts the (token, index) Diag for ALL SIX raise sites
in cli.sx, closing the two the reviewer found still unasserted:
- zero-arg UnknownCommand: parse([], ...) -> index -1, token ""
(the args.len == 0 sub-branch of cli.sx:237, distinct from the
one-arg too-few form already covered at index 0 / token args[0]).
- TooManyFlags (cli.sx:256): a command declaring 17 flag specs (> the
inline 16 cap) is rejected, not truncated -> index -1, token command.
The three index==-1 cases (zero-arg, too-many, missing-req) seed their
Diag with a sentinel before parse, so each assertion proves parse WROTE
the -1/"" rather than merely matching the `.{}` default. Verified
non-vacuous: flipping any expected value makes that line FAIL.
Test-only: cli.sx logic and src/ are untouched.
This commit is contained in:
@@ -106,6 +106,7 @@ main :: () -> ! {
|
||||
report("dash-value", dpv.value_of("out") == "-5" and dpv.is_set("verbose"));
|
||||
|
||||
// ── 7. Failures: each surfaces the specific variant ──────────────
|
||||
a_zero_args : []string = .[]; // nothing at all
|
||||
a_unknown_cmd : []string = .["ci", "deploy", "--out", "x"];
|
||||
a_unknown_group : []string = .["zz", "publish", "--out", "x"];
|
||||
a_too_few : []string = .["ci"];
|
||||
@@ -113,22 +114,60 @@ main :: () -> ! {
|
||||
a_missing_value : []string = .["ci", "publish", "--out"];
|
||||
a_value_eats : []string = .["ci", "publish", "--out", "--verbose"];
|
||||
a_missing_req : []string = .["ci", "publish", "--verbose"];
|
||||
report("err-unknown-cmd", raises(a_unknown_cmd, cmds, error.UnknownCommand));
|
||||
report("err-unknown-group", raises(a_unknown_group, cmds, error.UnknownCommand));
|
||||
report("err-too-few", raises(a_too_few, cmds, error.UnknownCommand));
|
||||
report("err-unknown-flag", raises(a_unknown_flag, cmds, error.UnknownFlag));
|
||||
report("err-missing-value", raises(a_missing_value, cmds, error.MissingValue));
|
||||
report("err-value-eats-flag", raises(a_value_eats, cmds, error.MissingValue));
|
||||
report("err-missing-req", raises(a_missing_req, cmds, error.MissingRequired));
|
||||
|
||||
// ── 8. Diag pins the offending (token, index) for EVERY error case ─
|
||||
// A command whose FlagSpec list exceeds the inline `Parsed.values` cap
|
||||
// (16): the parser rejects it with TooManyFlags rather than silently
|
||||
// truncating. 17 specs (> 16) trips the check right after dispatch
|
||||
// matches (group, command), before any flag is read.
|
||||
over_flags : []FlagSpec = .[
|
||||
FlagSpec.{ name = "f00", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f01", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f02", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f03", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f04", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f05", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f06", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f07", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f08", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f09", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f10", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f11", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f12", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f13", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f14", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f15", takes_value = false, required = false },
|
||||
FlagSpec.{ name = "f16", takes_value = false, required = false },
|
||||
];
|
||||
over_cmds : []Command = .[ Command.{ group = "big", command = "cmd", flags = over_flags } ];
|
||||
over_args : []string = .["big", "cmd"];
|
||||
|
||||
report("err-zero-args", raises(a_zero_args, cmds, error.UnknownCommand));
|
||||
report("err-unknown-cmd", raises(a_unknown_cmd, cmds, error.UnknownCommand));
|
||||
report("err-unknown-group", raises(a_unknown_group, cmds, error.UnknownCommand));
|
||||
report("err-too-few", raises(a_too_few, cmds, error.UnknownCommand));
|
||||
report("err-unknown-flag", raises(a_unknown_flag, cmds, error.UnknownFlag));
|
||||
report("err-missing-value", raises(a_missing_value, cmds, error.MissingValue));
|
||||
report("err-value-eats-flag", raises(a_value_eats, cmds, error.MissingValue));
|
||||
report("err-missing-req", raises(a_missing_req, cmds, error.MissingRequired));
|
||||
report("err-too-many-flags", raises(over_args, over_cmds, error.TooManyFlags));
|
||||
|
||||
// ── 8. Diag pins the offending (token, index) for EVERY raise site ─
|
||||
// Each failure records the exact offending token (a VIEW into `args`,
|
||||
// except a missing-required names the matched spec's flag name) plus
|
||||
// its `args` index, so a caller can report which token failed. An
|
||||
// unknown (group, command) PAIR — group wrong OR command wrong — pins
|
||||
// the COMMAND token at index 1, since the pair as a whole is what
|
||||
// failed to match; a missing-required uses index -1 (the token is a
|
||||
// flag name, not an `args` slot).
|
||||
// except missing-required / too-many which name the spec's flag / the
|
||||
// command) plus its `args` index, so a caller can report which token
|
||||
// failed. This covers ALL SIX raise sites in cli.sx, both UnknownCommand
|
||||
// sub-branches included:
|
||||
// - zero-arg -> index -1, token "" (args.len == 0)
|
||||
// - too-few -> index 0, token args[0] (args.len == 1)
|
||||
// - unknown pair -> index 1, token command (group OR command wrong)
|
||||
// - too-many -> index -1, token command (spec count > 16 cap)
|
||||
// - unknown flag -> index i, token flag tok
|
||||
// - missing val -> index i, token flag tok
|
||||
// - missing req -> index -1, token flag name
|
||||
// The three index==-1 cases (zero-arg, too-many, missing-req) COINCIDE
|
||||
// with `Diag`'s `.{}` defaults (index -1, token ""), so those Diags are
|
||||
// seeded with a sentinel first: the assertion then proves `parse`
|
||||
// actually WROTE the value, not that it merely left the default.
|
||||
de : Diag = .{};
|
||||
_, ue := parse(a_unknown_flag, cmds, @de);
|
||||
report("diag-flag-tag", ue == error.UnknownFlag);
|
||||
@@ -149,6 +188,11 @@ main :: () -> ! {
|
||||
report("diag-too-few-tag", fe == error.UnknownCommand);
|
||||
report("diag-too-few-token", df.token == "ci" and df.index == 0);
|
||||
|
||||
d0 : Diag = .{ index = 999, token = "<unset>" }; // sentinel: -1/"" are defaults
|
||||
_, z0e := parse(a_zero_args, cmds, @d0);
|
||||
report("diag-zero-args-tag", z0e == error.UnknownCommand);
|
||||
report("diag-zero-args-token", d0.token == "" and d0.index == -1);
|
||||
|
||||
dv : Diag = .{};
|
||||
_, ve := parse(a_missing_value, cmds, @dv);
|
||||
report("diag-missing-value-tag", ve == error.MissingValue);
|
||||
@@ -159,12 +203,17 @@ main :: () -> ! {
|
||||
report("diag-value-eats-tag", ze == error.MissingValue);
|
||||
report("diag-value-eats-token", dz.token == "--out" and dz.index == 2);
|
||||
|
||||
dm : Diag = .{};
|
||||
dm : Diag = .{ index = 999, token = "<unset>" }; // sentinel: -1 is the default
|
||||
_, me := parse(a_missing_req, cmds, @dm);
|
||||
report("diag-req-tag", me == error.MissingRequired);
|
||||
report("diag-req-token", dm.token == "out");
|
||||
report("diag-req-index", dm.index == -1);
|
||||
|
||||
dt : Diag = .{ index = 999, token = "<unset>" }; // sentinel: -1 is the default
|
||||
_, te := parse(over_args, over_cmds, @dt);
|
||||
report("diag-too-many-tag", te == error.TooManyFlags);
|
||||
report("diag-too-many-token", dt.token == "cmd" and dt.index == -1);
|
||||
|
||||
print("=== DONE ===\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user