feat(xce-test-cli-ux): add flag typo-suggestion and help-block renderer
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
This commit is contained in:
parent
0748f10cc6
commit
89af46af3e
4 changed files with 335 additions and 17 deletions
|
|
@ -375,6 +375,116 @@ static void test_reject_no_argument_flag_given_a_value_reports_distinctly(void)
|
|||
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__);
|
||||
|
|
@ -390,4 +500,9 @@ void test_flagspec(void)
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue