You are browsing as a guest. Sign up (or log in) to start making projects!

3h 27m 22s logged

Devlog #10: Unit tests for CLI argument parser (Commit: 90120d4b79)

5 changed files with 1448 additions and 11 deletions

The last core module got its tests. 73 new tests for the CLI argument parser, which makes 341 tests across 7 suites. The parser has more unit tests than any other function in any suite.


Schema(s) for the tests

To test the parser you need something to parse, so I built a test schema that covers every option type.

The main test schema has four options, each of a different type. Additionally, I made a few deliberately broken schemas for the failure paths: duplicate long names, duplicate short names, an empty schema, one with 33 options (one over the max), and one whose offset_in_config points straight out of the struct.


Small fixes uncovered along the way

Writing the tests exposed two issues worth mentioning.

ZINN_EXPECT_DOUBLE_EQ dumped its constexpr double EPSILON into the surrounding scope. As soon as you’d use it twice in one scope, you’d run into redefinition errors. I simply wrapped the macro’s contents in a do {} while(0) loop to prevent this from happening.

The default config values were magic numbers. Now they’re named constants (ZINN_CLI_DEFAULT_WIDTH, …) living in the header, and the tests assert against those instead of hardcoded values.


What the tests cover

The zinn_cli_parse tests take up most at 65. The remaining 8 are split between 4 other functions, each with one nullptr path and happy-path.

Schema validation and null checks (8 tests)

Every error path of zinn_cli_parse: null schema, null argv, null config, plus each of the broken schemas from the harness, all bail with the correct error code.

The help and version prescan (10 tests)

This section is the interesting one, because the pre-scan for --help/--version is intentionally naive: it scans for the flag before any real parsing. So zinn --str --help returns OK with help set, even though --str is missing its required value. The tests pin down ordering: --version --help gives version, --help --version gives help, since the loop exits on the first hit. zinn -- --help will toggle the help boolean and skip the rest of the parse.

The last case is a bug. In “./prog – –help” the pre-scan should identify the -- delimiter and exit the pre-scan loop without toggling the help boolean, thus letting it be parsed as a positional argument.

Option forms (6 tests)

Everything the parser accepts: --width 80, --width=80, -w 80, -w80, and mixed forms in a single invocation. Bundled values handle signs too, so -n-42 parses to -42.

-shello (short for –str hello) also works correctly:)

Positional arguments (10 tests)

Positionals fill input_path then output_path; a third one errors out. After -- the parser treats everything as a positional argument.

Fun fact, if you pass -- twice, both get consumed as the “positional mode switcher”.

Value and error handling (16 tests)

An int option given abc, overflow past INT64_MAX, 1e309 against a double, values that themselves look like flags (--num --flg), empty values (--str=, --num=), and --str=a=b, where everything after the first equals sign becomes the value.

Unknown options (3 tests)

Both flag variants are asserted to reject unknown identifiers.

Repeats and required options (12 tests)

Last occurrence wins. Required option checking happens after the full parse.


What’s next?

With the final core module’s unit tests now done, I plan to switch build systems so the engine can work on both Linux and Windows flawlessly, which is a prerequisite for the beautiful media-to-ASCII pipeline.

0
6

Comments 0

No comments yet. Be the first!