test: extend coverage of shellTest
This commit is contained in:
1 parent
a5176fe8d9
commit
2681016ffb
1 file changed
+153
+153
@@ -154,3 +154,156 @@ KTEST(shellSplitPipelineStagesTooManyStagesReturnsNegativeOneWithoutMutating)
|
|||||||
KT_STREQ(t, text, "a | b | c | d | e");
|
KT_STREQ(t, text, "a | b | c | d | e");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* shellSplitAndOr (shell.c) is the pure-string half of the shell's new
|
||||||
|
`cmd1 && cmd2 || cmd3 ...` conditional-chaining syntax -- deciding
|
||||||
|
whether execute() should short-circuit through several commands' real
|
||||||
|
exit statuses (executeSingle, shell.c) or fall back to a single
|
||||||
|
command-or-pipeline exactly as before, run BEFORE pipeline splitting
|
||||||
|
(`|` binds tighter than `&&`/`||`), the same "pure logic, no
|
||||||
|
VFS/scheduler" split every other parser above already has. */
|
||||||
|
|
||||||
|
KTEST(shellSplitAndOrNoOperatorReturnsZero)
|
||||||
|
{
|
||||||
|
char text[] = "ls -l";
|
||||||
|
char* segments[shellMaxAndOrSegments];
|
||||||
|
ShellAndOrOp ops[shellMaxAndOrSegments - 1];
|
||||||
|
int count = shellSplitAndOr(text, segments, ops, shellMaxAndOrSegments);
|
||||||
|
KT_EQ(t, count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(shellSplitAndOrBasicAndSplit)
|
||||||
|
{
|
||||||
|
char text[] = "mkdir /tmp/a && echo ok";
|
||||||
|
char* segments[shellMaxAndOrSegments];
|
||||||
|
ShellAndOrOp ops[shellMaxAndOrSegments - 1];
|
||||||
|
int count = shellSplitAndOr(text, segments, ops, shellMaxAndOrSegments);
|
||||||
|
KT_EQ(t, count, 2);
|
||||||
|
KT_STREQ(t, segments[0], "mkdir /tmp/a");
|
||||||
|
KT_STREQ(t, segments[1], "echo ok");
|
||||||
|
KT_EQ(t, ops[0], shellAndOrAnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(shellSplitAndOrBasicOrSplit)
|
||||||
|
{
|
||||||
|
char text[] = "false || echo fallback";
|
||||||
|
char* segments[shellMaxAndOrSegments];
|
||||||
|
ShellAndOrOp ops[shellMaxAndOrSegments - 1];
|
||||||
|
int count = shellSplitAndOr(text, segments, ops, shellMaxAndOrSegments);
|
||||||
|
KT_EQ(t, count, 2);
|
||||||
|
KT_STREQ(t, segments[0], "false");
|
||||||
|
KT_STREQ(t, segments[1], "echo fallback");
|
||||||
|
KT_EQ(t, ops[0], shellAndOrOr);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(shellSplitAndOrMixedChainRecordsEachOperatorIndependently)
|
||||||
|
{
|
||||||
|
/* The classic shell if-then-else idiom: "run b only if a succeeded,
|
||||||
|
run c only if the segment that actually ran last (a or b) did NOT
|
||||||
|
succeed" -- proves && and || are tracked as two independently
|
||||||
|
recorded operators, not collapsed into one boolean. */
|
||||||
|
char text[] = "a && b || c";
|
||||||
|
char* segments[shellMaxAndOrSegments];
|
||||||
|
ShellAndOrOp ops[shellMaxAndOrSegments - 1];
|
||||||
|
int count = shellSplitAndOr(text, segments, ops, shellMaxAndOrSegments);
|
||||||
|
KT_EQ(t, count, 3);
|
||||||
|
KT_STREQ(t, segments[0], "a");
|
||||||
|
KT_STREQ(t, segments[1], "b");
|
||||||
|
KT_STREQ(t, segments[2], "c");
|
||||||
|
KT_EQ(t, ops[0], shellAndOrAnd);
|
||||||
|
KT_EQ(t, ops[1], shellAndOrOr);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(shellSplitAndOrTrimsSpacesAroundEachOperator)
|
||||||
|
{
|
||||||
|
char text[] = "a&&b";
|
||||||
|
char* segments[shellMaxAndOrSegments];
|
||||||
|
ShellAndOrOp ops[shellMaxAndOrSegments - 1];
|
||||||
|
int count = shellSplitAndOr(text, segments, ops, shellMaxAndOrSegments);
|
||||||
|
KT_EQ(t, count, 2);
|
||||||
|
KT_STREQ(t, segments[0], "a");
|
||||||
|
KT_STREQ(t, segments[1], "b");
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(shellSplitAndOrLeavesSingleTopLevelPipeInsideItsOwnSegment)
|
||||||
|
{
|
||||||
|
/* '|' binds tighter than '&&'/'||': a lone pipe character must never be
|
||||||
|
mistaken for half of '||' (which only matches two CONSECUTIVE '|'
|
||||||
|
characters), so "a | b" stays a single, whole segment here -- only
|
||||||
|
execute()'s own later shellSplitPipelineStages call on that segment
|
||||||
|
ever splits it further. */
|
||||||
|
char text[] = "a | b && c";
|
||||||
|
char* segments[shellMaxAndOrSegments];
|
||||||
|
ShellAndOrOp ops[shellMaxAndOrSegments - 1];
|
||||||
|
int count = shellSplitAndOr(text, segments, ops, shellMaxAndOrSegments);
|
||||||
|
KT_EQ(t, count, 2);
|
||||||
|
KT_STREQ(t, segments[0], "a | b");
|
||||||
|
KT_STREQ(t, segments[1], "c");
|
||||||
|
KT_EQ(t, ops[0], shellAndOrAnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(shellSplitAndOrTooManySegmentsReturnsNegativeOneWithoutMutating)
|
||||||
|
{
|
||||||
|
char text[] = "a && b && c && d && e";
|
||||||
|
char* segments[shellMaxAndOrSegments];
|
||||||
|
ShellAndOrOp ops[shellMaxAndOrSegments - 1];
|
||||||
|
/* shellMaxAndOrSegments is 4, but this line has 5 segments -- must be
|
||||||
|
rejected outright, leaving `text` completely untouched, the same
|
||||||
|
"malformed, nothing changed" contract shellSplitPipelineStages' own
|
||||||
|
analogous limit already uses. */
|
||||||
|
int count = shellSplitAndOr(text, segments, ops, shellMaxAndOrSegments);
|
||||||
|
KT_EQ(t, count, -1);
|
||||||
|
KT_STREQ(t, text, "a && b && c && d && e");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shellExtractRedirects (shell.c) is the pure-token half of shell.c's new
|
||||||
|
`cmd [ARGS] [< IN] [> OUT | >> OUT]` file redirection -- exactly the same
|
||||||
|
"pure logic, no VFS/scheduler" split shellTokenize/shellSplitPipelineStages
|
||||||
|
already have from their own doc comments above, operating on
|
||||||
|
shellTokenize's own output shape (an already-split argv) instead of a
|
||||||
|
raw line. */
|
||||||
|
|
||||||
|
KTEST(shellExtractRedirectsNoOperatorsLeavesArgvUnchanged)
|
||||||
|
{
|
||||||
|
char text[] = "cat file.txt";
|
||||||
|
char* argv[8];
|
||||||
|
int argc = shellTokenize(text, argv, 8);
|
||||||
|
ShellRedirect redirect;
|
||||||
|
bool ok = shellExtractRedirects(argv, &argc, &redirect);
|
||||||
|
KT_TRUE(t, ok);
|
||||||
|
KT_EQ(t, argc, 2);
|
||||||
|
KT_STREQ(t, argv[0], "cat");
|
||||||
|
KT_STREQ(t, argv[1], "file.txt");
|
||||||
|
KT_NULL(t, redirect.inPath);
|
||||||
|
KT_NULL(t, redirect.outPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(shellExtractRedirectsOutputTruncate)
|
||||||
|
{
|
||||||
|
char text[] = "sort > out.txt";
|
||||||
|
char* argv[8];
|
||||||
|
int argc = shellTokenize(text, argv, 8);
|
||||||
|
ShellRedirect redirect;
|
||||||
|
bool ok = shellExtractRedirects(argv, &argc, &redirect);
|
||||||
|
KT_TRUE(t, ok);
|
||||||
|
KT_EQ(t, argc, 1);
|
||||||
|
KT_STREQ(t, argv[0], "sort");
|
||||||
|
KT_NULL(t, redirect.inPath);
|
||||||
|
KT_NOTNULL(t, redirect.outPath);
|
||||||
|
KT_STREQ(t, redirect.outPath, "out.txt");
|
||||||
|
KT_FALSE(t, redirect.appendOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(shellExtractRedirectsOutputAppend)
|
||||||
|
{
|
||||||
|
char text[] = "sort >> out.txt";
|
||||||
|
char* argv[8];
|
||||||
|
int argc = shellTokenize(text, argv, 8);
|
||||||
|
ShellRedirect redirect;
|
||||||
|
bool ok = shellExtractRedirects(argv, &argc, &redirect);
|
||||||
|
KT_TRUE(t, ok);
|
||||||
|
KT_EQ(t, argc, 1);
|
||||||
|
KT_STREQ(t, argv[0], "sort");
|
||||||
|
KT_STREQ(t, redirect.outPath, "out.txt");
|
||||||
|
KT_TRUE(t, redirect.appendOut);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user