flagspec_reject() now names a likely-intended flag when an unrecognized flag closely matches exactly one entry in the descriptor table (genuine Levenshtein DP, silent on zero or ambiguous matches). flagspec_render_help() generates the short/long/required-optional/default flag-listing block from a descriptor table. 1278/1278 controller suite passing (+5). Task 1.2 of .kiro/specs/xce-test-cli-ux/tasks.md. Requirements: 6.1, 6.2, 6.3, 6.4, 8.2, 10.1, 10.2, 10.3 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019jaMf9QEwpugH2W9KAuc7f
508 lines
19 KiB
C
508 lines
19 KiB
C
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <unity/unity.h>
|
|
|
|
#include "flagspec.h"
|
|
|
|
/* ---- fixture: a made-up two-flag test command -------------------------
|
|
* "testcmd -h <host> [-c <cmdline>]" -- one required flag with a short
|
|
* and long form, one optional flag with a short and long form. Used by
|
|
* every test below so the same descriptor table exercises both
|
|
* flagspec_build_opts() and flagspec_reject(), and so a rejection's usage
|
|
* summary is provably the SAME string no matter which of the two flags is
|
|
* the malformed one (the task's stated observable-completion criterion).
|
|
* ------------------------------------------------------------------- */
|
|
|
|
static const flagspec_t g_testcmd_spec[2] = {
|
|
{ 'h', "host", "host", required_argument, 1, NULL },
|
|
{ 'c', "command", "command-line", no_argument, 0, "off" },
|
|
};
|
|
|
|
static const char g_testcmd_usage[] = "testcmd -h <host> [-c]";
|
|
|
|
/* ---- stream-capture helpers --------------------------------------------
|
|
* Mirrors test_rcfile.c's dup/dup2/freopen technique for proving what a
|
|
* function wrote to stderr, extended to capture stdout AND stderr at the
|
|
* same time so a test can prove Requirement 3.1: a rejection message
|
|
* lands on stderr only, never stdout. Uses a fresh temp dir per test
|
|
* (mkdtemp, like test_rcfile.c/test_configcompare.c's fresh_dir()) so
|
|
* capture files never collide with anything else in this shared /tmp. */
|
|
|
|
static char g_dir[256];
|
|
|
|
static void fresh_dir(void)
|
|
{
|
|
strcpy(g_dir, "/tmp/xce_flagspec_test_XXXXXX");
|
|
TEST_ASSERT_NOT_NULL(mkdtemp(g_dir));
|
|
}
|
|
|
|
static void cleanup_dir(void)
|
|
{
|
|
char cmd[512];
|
|
snprintf(cmd, sizeof(cmd), "rm -rf %s", g_dir);
|
|
system(cmd);
|
|
}
|
|
|
|
typedef struct {
|
|
int saved_stdout_fd;
|
|
int saved_stderr_fd;
|
|
char stdout_path[300];
|
|
char stderr_path[300];
|
|
} capture_t;
|
|
|
|
static void capture_begin(capture_t* cap)
|
|
{
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
|
|
cap->saved_stdout_fd = dup(fileno(stdout));
|
|
cap->saved_stderr_fd = dup(fileno(stderr));
|
|
TEST_ASSERT_TRUE(cap->saved_stdout_fd >= 0);
|
|
TEST_ASSERT_TRUE(cap->saved_stderr_fd >= 0);
|
|
|
|
snprintf(cap->stdout_path, sizeof(cap->stdout_path), "%s/out", g_dir);
|
|
snprintf(cap->stderr_path, sizeof(cap->stderr_path), "%s/err", g_dir);
|
|
|
|
TEST_ASSERT_NOT_NULL(freopen(cap->stdout_path, "w", stdout));
|
|
TEST_ASSERT_NOT_NULL(freopen(cap->stderr_path, "w", stderr));
|
|
}
|
|
|
|
/* Restores the real stdout/stderr fds and reads both captured files back
|
|
* into caller-provided, NUL-terminated buffers. */
|
|
static void capture_end(capture_t* cap, char* out_stdout,
|
|
size_t out_stdout_cap, char* out_stderr,
|
|
size_t out_stderr_cap)
|
|
{
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
TEST_ASSERT_TRUE(dup2(cap->saved_stdout_fd, fileno(stdout)) >= 0);
|
|
TEST_ASSERT_TRUE(dup2(cap->saved_stderr_fd, fileno(stderr)) >= 0);
|
|
close(cap->saved_stdout_fd);
|
|
close(cap->saved_stderr_fd);
|
|
clearerr(stdout);
|
|
clearerr(stderr);
|
|
|
|
FILE* f;
|
|
size_t n;
|
|
|
|
f = fopen(cap->stdout_path, "r");
|
|
TEST_ASSERT_NOT_NULL(f);
|
|
n = fread(out_stdout, 1, out_stdout_cap - 1, f);
|
|
out_stdout[n] = '\0';
|
|
fclose(f);
|
|
|
|
f = fopen(cap->stderr_path, "r");
|
|
TEST_ASSERT_NOT_NULL(f);
|
|
n = fread(out_stderr, 1, out_stderr_cap - 1, f);
|
|
out_stderr[n] = '\0';
|
|
fclose(f);
|
|
}
|
|
|
|
/* ---- flagspec_build_opts() tests --------------------------------------- */
|
|
|
|
static void test_build_opts_produces_correct_table_for_two_flag_command(void)
|
|
{
|
|
struct option opts[3];
|
|
char optstring[8];
|
|
|
|
int rc = flagspec_build_opts(g_testcmd_spec, 2, opts, optstring,
|
|
sizeof(optstring));
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
|
|
TEST_ASSERT_EQUAL_STRING("host", opts[0].name);
|
|
TEST_ASSERT_EQUAL_INT(required_argument, opts[0].has_arg);
|
|
TEST_ASSERT_NULL(opts[0].flag);
|
|
TEST_ASSERT_EQUAL_INT('h', opts[0].val);
|
|
|
|
TEST_ASSERT_EQUAL_STRING("command", opts[1].name);
|
|
TEST_ASSERT_EQUAL_INT(no_argument, opts[1].has_arg);
|
|
TEST_ASSERT_NULL(opts[1].flag);
|
|
TEST_ASSERT_EQUAL_INT('c', opts[1].val);
|
|
|
|
/* trailing all-zero sentinel getopt_long() requires */
|
|
TEST_ASSERT_NULL(opts[2].name);
|
|
TEST_ASSERT_EQUAL_INT(0, opts[2].has_arg);
|
|
TEST_ASSERT_NULL(opts[2].flag);
|
|
TEST_ASSERT_EQUAL_INT(0, opts[2].val);
|
|
|
|
TEST_ASSERT_EQUAL_STRING("h:c", optstring);
|
|
}
|
|
|
|
static void test_build_opts_assigns_synthetic_val_to_long_only_flag(void)
|
|
{
|
|
flagspec_t spec[3];
|
|
memcpy(spec, g_testcmd_spec, sizeof(g_testcmd_spec));
|
|
spec[2] = (flagspec_t){ 0, "json", "output-format", no_argument, 0,
|
|
"table" };
|
|
|
|
struct option opts[4];
|
|
char optstring[8];
|
|
|
|
int rc = flagspec_build_opts(spec, 3, opts, optstring,
|
|
sizeof(optstring));
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_STRING("json", opts[2].name);
|
|
TEST_ASSERT_EQUAL_INT(no_argument, opts[2].has_arg);
|
|
TEST_ASSERT_EQUAL_INT(FLAGSPEC_LONG_ONLY_BASE + 2, opts[2].val);
|
|
/* a long-only flag contributes nothing to the short optstring */
|
|
TEST_ASSERT_EQUAL_STRING("h:c", optstring);
|
|
|
|
/* still terminated correctly at the new, later index */
|
|
TEST_ASSERT_NULL(opts[3].name);
|
|
TEST_ASSERT_EQUAL_INT(0, opts[3].val);
|
|
}
|
|
|
|
static void test_build_opts_rejects_null_spec(void)
|
|
{
|
|
struct option opts[3];
|
|
char optstring[8];
|
|
TEST_ASSERT_EQUAL_INT(-EINVAL,
|
|
flagspec_build_opts(NULL, 2, opts, optstring,
|
|
sizeof(optstring)));
|
|
}
|
|
|
|
static void test_build_opts_rejects_zero_count(void)
|
|
{
|
|
struct option opts[3];
|
|
char optstring[8];
|
|
TEST_ASSERT_EQUAL_INT(
|
|
-EINVAL, flagspec_build_opts(g_testcmd_spec, 0, opts, optstring,
|
|
sizeof(optstring)));
|
|
}
|
|
|
|
static void test_build_opts_rejects_entry_with_null_long_opt(void)
|
|
{
|
|
flagspec_t bad_spec[1] = {
|
|
{ 'x', NULL, "x", no_argument, 0, NULL },
|
|
};
|
|
struct option opts[2];
|
|
char optstring[8];
|
|
TEST_ASSERT_EQUAL_INT(
|
|
-EINVAL,
|
|
flagspec_build_opts(bad_spec, 1, opts, optstring, sizeof(optstring)));
|
|
}
|
|
|
|
static void test_build_opts_rejects_too_small_optstring_buffer(void)
|
|
{
|
|
struct option opts[3];
|
|
/* g_testcmd_spec builds "h:c" (h requires a value, c does not) --
|
|
* exactly 4 bytes including the terminating NUL. A 3-byte buffer is
|
|
* one short. */
|
|
char optstring[3];
|
|
|
|
TEST_ASSERT_EQUAL_INT(-ENOSPC,
|
|
flagspec_build_opts(g_testcmd_spec, 2, opts,
|
|
optstring, sizeof(optstring)));
|
|
}
|
|
|
|
/* ---- flagspec_reject() tests: real getopt_long() pipeline -------------
|
|
* Each case builds the real option table via flagspec_build_opts(), then
|
|
* drives an actual getopt_long() call over a fabricated argv so the
|
|
* bad_flag handed to flagspec_reject() is exactly what a real cmd_*
|
|
* caller would see from its own `case '?':` arm -- not a hand-picked
|
|
* string. */
|
|
|
|
static int run_getopt_long_expect_error(int argc, char* argv[],
|
|
const struct option* opts,
|
|
const char* optstring)
|
|
{
|
|
optind = 1;
|
|
opterr = 0;
|
|
int opt = getopt_long(argc, argv, optstring, opts, NULL);
|
|
TEST_ASSERT_EQUAL_INT('?', opt);
|
|
return optind;
|
|
}
|
|
|
|
static void test_reject_rejects_unrecognized_long_flag(void)
|
|
{
|
|
struct option opts[3];
|
|
char optstring[8];
|
|
TEST_ASSERT_EQUAL_INT(0, flagspec_build_opts(g_testcmd_spec, 2, opts,
|
|
optstring,
|
|
sizeof(optstring)));
|
|
|
|
char* argv[] = { "testcmd", "--bogus", NULL };
|
|
int after = run_getopt_long_expect_error(2, argv, opts, optstring);
|
|
const char* bad_flag = argv[after - 1];
|
|
TEST_ASSERT_EQUAL_STRING("--bogus", bad_flag);
|
|
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_testcmd_spec, 2, bad_flag,
|
|
g_testcmd_usage);
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "unrecognized flag '--bogus'"));
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "usage: testcmd -h <host> [-c]"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
static void test_reject_rejects_unrecognized_short_flag(void)
|
|
{
|
|
struct option opts[3];
|
|
char optstring[8];
|
|
TEST_ASSERT_EQUAL_INT(0, flagspec_build_opts(g_testcmd_spec, 2, opts,
|
|
optstring,
|
|
sizeof(optstring)));
|
|
|
|
char* argv[] = { "testcmd", "-z", NULL };
|
|
int after = run_getopt_long_expect_error(2, argv, opts, optstring);
|
|
const char* bad_flag = argv[after - 1];
|
|
TEST_ASSERT_EQUAL_STRING("-z", bad_flag);
|
|
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_testcmd_spec, 2, bad_flag,
|
|
g_testcmd_usage);
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "unrecognized flag '-z'"));
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "usage: testcmd -h <host> [-c]"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
static void test_reject_rejects_missing_value_long_flag(void)
|
|
{
|
|
struct option opts[3];
|
|
char optstring[8];
|
|
TEST_ASSERT_EQUAL_INT(0, flagspec_build_opts(g_testcmd_spec, 2, opts,
|
|
optstring,
|
|
sizeof(optstring)));
|
|
|
|
/* "--host" is the last argv element -- getopt_long() has nothing to
|
|
* consume as its required value. */
|
|
char* argv[] = { "testcmd", "--host", NULL };
|
|
int after = run_getopt_long_expect_error(2, argv, opts, optstring);
|
|
const char* bad_flag = argv[after - 1];
|
|
TEST_ASSERT_EQUAL_STRING("--host", bad_flag);
|
|
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_testcmd_spec, 2, bad_flag,
|
|
g_testcmd_usage);
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "flag '--host' requires a value"));
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "usage: testcmd -h <host> [-c]"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
static void test_reject_rejects_missing_value_short_flag(void)
|
|
{
|
|
struct option opts[3];
|
|
char optstring[8];
|
|
TEST_ASSERT_EQUAL_INT(0, flagspec_build_opts(g_testcmd_spec, 2, opts,
|
|
optstring,
|
|
sizeof(optstring)));
|
|
|
|
char* argv[] = { "testcmd", "-h", NULL };
|
|
int after = run_getopt_long_expect_error(2, argv, opts, optstring);
|
|
const char* bad_flag = argv[after - 1];
|
|
TEST_ASSERT_EQUAL_STRING("-h", bad_flag);
|
|
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_testcmd_spec, 2, bad_flag,
|
|
g_testcmd_usage);
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "flag '-h' requires a value"));
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "usage: testcmd -h <host> [-c]"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
/* Direct-call (non-getopt_long) tests: exercises flagspec_reject()'s own
|
|
* argument handling in isolation, including NULL inputs it must survive
|
|
* without crashing. */
|
|
|
|
static void test_reject_handles_null_bad_flag_and_null_usage_gracefully(void)
|
|
{
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_testcmd_spec, 2, NULL, NULL);
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "unrecognized flag"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
static void test_reject_no_argument_flag_given_a_value_reports_distinctly(void)
|
|
{
|
|
/* "-c" never requires a value; a caller that somehow reaches
|
|
* flagspec_reject() with the bare "-c" token (matches an entry, but
|
|
* that entry takes no_argument) must be told a different thing than
|
|
* either the unrecognized-flag or missing-value message. */
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_testcmd_spec, 2, "-c",
|
|
g_testcmd_usage);
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "flag '-c' does not take a value"));
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "usage: testcmd -h <host> [-c]"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
/* ---- flagspec_reject() typo-suggestion tests (Requirement 10) ---------
|
|
* A second fixture, g_ambiguous_spec, is purpose-built for the
|
|
* ambiguous-match case below so its edit distances are deliberate rather
|
|
* than incidental to g_testcmd_spec's two very dissimilar flag names. */
|
|
|
|
static const flagspec_t g_ambiguous_spec[2] = {
|
|
{ 0, "targeta", "target-a", no_argument, 0, NULL },
|
|
{ 0, "targetb", "target-b", no_argument, 0, NULL },
|
|
};
|
|
|
|
static void test_reject_suggests_single_close_match_flag_typo(void)
|
|
{
|
|
/* "--hst" is "--host" with the 'o' deleted -- one edit, well inside
|
|
* the closeness threshold for a 6-character long-form spelling -- and
|
|
* is nowhere near "--command". Requirement 10.1: name the
|
|
* likely-intended flag in the rejection message. */
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_testcmd_spec, 2, "--hst",
|
|
g_testcmd_usage);
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "unrecognized flag '--hst'"));
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "did you mean"));
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "--host"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
static void test_reject_gives_no_suggestion_for_a_distant_typo(void)
|
|
{
|
|
/* "--wombat" does not closely resemble "--host" or "--command" by any
|
|
* reasonable edit-distance threshold. Requirement 10.2: report
|
|
* unrecognized without guessing. */
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_testcmd_spec, 2, "--wombat",
|
|
g_testcmd_usage);
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "unrecognized flag '--wombat'"));
|
|
TEST_ASSERT_NULL(strstr(err_buf, "did you mean"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
static void test_reject_gives_no_suggestion_when_typo_is_ambiguous(void)
|
|
{
|
|
/* "--targetc" is exactly one substitution away from BOTH "--targeta"
|
|
* and "--targetb". Requirement 10.1 only promises a guess when
|
|
* exactly one flag qualifies -- two equally-plausible candidates must
|
|
* produce no suggestion at all, the same as a typo that matches
|
|
* nothing. */
|
|
fresh_dir();
|
|
capture_t cap;
|
|
capture_begin(&cap);
|
|
int rc = flagspec_reject("testcmd", g_ambiguous_spec, 2, "--targetc",
|
|
"testcmd --targeta|--targetb");
|
|
char out_buf[512], err_buf[512];
|
|
capture_end(&cap, out_buf, sizeof(out_buf), err_buf, sizeof(err_buf));
|
|
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
TEST_ASSERT_EQUAL_STRING("", out_buf);
|
|
TEST_ASSERT_NOT_NULL(strstr(err_buf, "unrecognized flag '--targetc'"));
|
|
TEST_ASSERT_NULL(strstr(err_buf, "did you mean"));
|
|
cleanup_dir();
|
|
}
|
|
|
|
/* ---- flagspec_render_help() tests (Requirement 8.2) -------------------- */
|
|
|
|
static void test_render_help_formats_multi_flag_table(void)
|
|
{
|
|
flagspec_t spec[3];
|
|
memcpy(spec, g_testcmd_spec, sizeof(g_testcmd_spec));
|
|
spec[2] = (flagspec_t){ 0, "json", "output-format", no_argument, 0,
|
|
"table" };
|
|
|
|
char* help = flagspec_render_help(spec, 3);
|
|
TEST_ASSERT_NOT_NULL(help);
|
|
|
|
/* required flag: short + long form, marked required */
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "-h"));
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "--host"));
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "required"));
|
|
|
|
/* optional flag: short + long form, marked optional, default shown */
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "-c"));
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "--command"));
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "optional"));
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "off"));
|
|
|
|
/* long-only flag: no short form, still shows long form + default */
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "--json"));
|
|
TEST_ASSERT_NOT_NULL(strstr(help, "table"));
|
|
|
|
free(help);
|
|
}
|
|
|
|
static void test_render_help_rejects_null_and_empty_inputs(void)
|
|
{
|
|
TEST_ASSERT_NULL(flagspec_render_help(NULL, 2));
|
|
TEST_ASSERT_NULL(flagspec_render_help(g_testcmd_spec, 0));
|
|
}
|
|
|
|
void test_flagspec(void)
|
|
{
|
|
UnitySetTestFile(__FILE__);
|
|
RUN_TEST(test_build_opts_produces_correct_table_for_two_flag_command);
|
|
RUN_TEST(test_build_opts_assigns_synthetic_val_to_long_only_flag);
|
|
RUN_TEST(test_build_opts_rejects_null_spec);
|
|
RUN_TEST(test_build_opts_rejects_zero_count);
|
|
RUN_TEST(test_build_opts_rejects_entry_with_null_long_opt);
|
|
RUN_TEST(test_build_opts_rejects_too_small_optstring_buffer);
|
|
RUN_TEST(test_reject_rejects_unrecognized_long_flag);
|
|
RUN_TEST(test_reject_rejects_unrecognized_short_flag);
|
|
RUN_TEST(test_reject_rejects_missing_value_long_flag);
|
|
RUN_TEST(test_reject_rejects_missing_value_short_flag);
|
|
RUN_TEST(test_reject_handles_null_bad_flag_and_null_usage_gracefully);
|
|
RUN_TEST(test_reject_no_argument_flag_given_a_value_reports_distinctly);
|
|
RUN_TEST(test_reject_suggests_single_close_match_flag_typo);
|
|
RUN_TEST(test_reject_gives_no_suggestion_for_a_distant_typo);
|
|
RUN_TEST(test_reject_gives_no_suggestion_when_typo_is_ambiguous);
|
|
RUN_TEST(test_render_help_formats_multi_flag_table);
|
|
RUN_TEST(test_render_help_rejects_null_and_empty_inputs);
|
|
}
|