test: cover shellTest
This commit is contained in:
1 parent
1916c7298b
commit
a5176fe8d9
1 file changed
+156
+156
@@ -0,0 +1,156 @@
|
||||
#include "ktest.h"
|
||||
#include "shell.h"
|
||||
#include "string.h"
|
||||
|
||||
/* shellTokenize (shell.c) is the pure-string half of the shell's new
|
||||
`NAME [ARGS] [&]` job-control dispatch (runProgram, shell.c) -- turning
|
||||
a typed line into a real argv[] the way a normal shell does, instead of
|
||||
the single "rest of the line" string every other built-in command is
|
||||
content with. Everything below runs before schedStart() (ktest.h's own
|
||||
file comment), so it deliberately never touches the VFS, jobctl.h, or a
|
||||
real spawn -- only the tokenizer itself, which is ordinary deterministic
|
||||
string manipulation. */
|
||||
|
||||
KTEST(shellTokenizeEmptyLine)
|
||||
{
|
||||
char text[] = "";
|
||||
char* tokens[4];
|
||||
int count = shellTokenize(text, tokens, 4);
|
||||
KT_EQ(t, count, 0);
|
||||
}
|
||||
|
||||
KTEST(shellTokenizeSingleWord)
|
||||
{
|
||||
char text[] = "hello";
|
||||
char* tokens[4];
|
||||
int count = shellTokenize(text, tokens, 4);
|
||||
KT_EQ(t, count, 1);
|
||||
KT_STREQ(t, tokens[0], "hello");
|
||||
}
|
||||
|
||||
KTEST(shellTokenizeMultipleWordsCollapsesRepeatedSpaces)
|
||||
{
|
||||
char text[] = " one two three ";
|
||||
char* tokens[8];
|
||||
int count = shellTokenize(text, tokens, 8);
|
||||
KT_EQ(t, count, 3);
|
||||
KT_STREQ(t, tokens[0], "one");
|
||||
KT_STREQ(t, tokens[1], "two");
|
||||
KT_STREQ(t, tokens[2], "three");
|
||||
}
|
||||
|
||||
KTEST(shellTokenizeAllSpacesYieldsNothing)
|
||||
{
|
||||
char text[] = " ";
|
||||
char* tokens[4];
|
||||
int count = shellTokenize(text, tokens, 4);
|
||||
KT_EQ(t, count, 0);
|
||||
}
|
||||
|
||||
KTEST(shellTokenizeRespectsCapacityInsteadOfOverrunningArray)
|
||||
{
|
||||
char text[] = "a b c d e";
|
||||
char* tokens[3];
|
||||
int count = shellTokenize(text, tokens, 3);
|
||||
KT_EQ(t, count, 3);
|
||||
KT_STREQ(t, tokens[0], "a");
|
||||
KT_STREQ(t, tokens[1], "b");
|
||||
KT_STREQ(t, tokens[2], "c");
|
||||
}
|
||||
|
||||
KTEST(shellTokenizeTrailingAmpersandIsJustAnotherToken)
|
||||
{
|
||||
/* runProgram (shell.c) is the one that gives "&" its background
|
||||
meaning by inspecting the LAST token itself -- shellTokenize's own
|
||||
job is only to split words apart, so it must hand "&" back
|
||||
unchanged like any other token, not special-case it. */
|
||||
char text[] = "sleepjob arg1 &";
|
||||
char* tokens[8];
|
||||
int count = shellTokenize(text, tokens, 8);
|
||||
KT_EQ(t, count, 3);
|
||||
KT_STREQ(t, tokens[0], "sleepjob");
|
||||
KT_STREQ(t, tokens[1], "arg1");
|
||||
KT_STREQ(t, tokens[2], "&");
|
||||
}
|
||||
|
||||
KTEST(shellTokenizeZeroCapacityYieldsNothing)
|
||||
{
|
||||
char text[] = "one two";
|
||||
char* tokens[1];
|
||||
int count = shellTokenize(text, tokens, 0);
|
||||
KT_EQ(t, count, 0);
|
||||
}
|
||||
|
||||
/* shellSplitPipelineStages (shell.c) is the pure-string half of the shell's
|
||||
`cmd1 | cmd2 | ... | cmdN` pipeline syntax -- deciding whether execute()
|
||||
should run runPipelineStages (N spawned processes joined by N-1 real
|
||||
pipes) or fall back to a single command, exactly the same "pure logic, no
|
||||
VFS/scheduler" split shellTokenize already has from its own doc comment
|
||||
above. */
|
||||
|
||||
KTEST(shellSplitPipelineStagesNoPipeReturnsZero)
|
||||
{
|
||||
char text[] = "ls -l";
|
||||
char* stages[shellMaxPipelineStages];
|
||||
int count = shellSplitPipelineStages(text, stages, shellMaxPipelineStages);
|
||||
KT_EQ(t, count, 0);
|
||||
}
|
||||
|
||||
KTEST(shellSplitPipelineStagesBasicTwoStageSplit)
|
||||
{
|
||||
char text[] = "producer|consumer";
|
||||
char* stages[shellMaxPipelineStages];
|
||||
int count = shellSplitPipelineStages(text, stages, shellMaxPipelineStages);
|
||||
KT_EQ(t, count, 2);
|
||||
KT_STREQ(t, stages[0], "producer");
|
||||
KT_STREQ(t, stages[1], "consumer");
|
||||
}
|
||||
|
||||
KTEST(shellSplitPipelineStagesTrimsSpacesAroundEachPipe)
|
||||
{
|
||||
char text[] = "pipesrc arg1 | pipecat | pipesink arg2";
|
||||
char* stages[shellMaxPipelineStages];
|
||||
int count = shellSplitPipelineStages(text, stages, shellMaxPipelineStages);
|
||||
KT_EQ(t, count, 3);
|
||||
KT_STREQ(t, stages[0], "pipesrc arg1");
|
||||
KT_STREQ(t, stages[1], "pipecat");
|
||||
KT_STREQ(t, stages[2], "pipesink arg2");
|
||||
}
|
||||
|
||||
KTEST(shellSplitPipelineStagesThreeStageSplitIsRealNotJustFirstPipe)
|
||||
{
|
||||
/* Unlike the old two-way shellSplitPipeline this generalizes, every
|
||||
top-level '|' is now recognized -- a real N-stage pipeline, not just
|
||||
the first split with the rest left embedded in one text blob. */
|
||||
char text[] = "a | b | c";
|
||||
char* stages[shellMaxPipelineStages];
|
||||
int count = shellSplitPipelineStages(text, stages, shellMaxPipelineStages);
|
||||
KT_EQ(t, count, 3);
|
||||
KT_STREQ(t, stages[0], "a");
|
||||
KT_STREQ(t, stages[1], "b");
|
||||
KT_STREQ(t, stages[2], "c");
|
||||
}
|
||||
|
||||
KTEST(shellSplitPipelineStagesEmptyLastStageYieldsEmptyString)
|
||||
{
|
||||
char text[] = "producer |";
|
||||
char* stages[shellMaxPipelineStages];
|
||||
int count = shellSplitPipelineStages(text, stages, shellMaxPipelineStages);
|
||||
KT_EQ(t, count, 2);
|
||||
KT_STREQ(t, stages[0], "producer");
|
||||
KT_STREQ(t, stages[1], "");
|
||||
}
|
||||
|
||||
KTEST(shellSplitPipelineStagesTooManyStagesReturnsNegativeOneWithoutMutating)
|
||||
{
|
||||
char text[] = "a | b | c | d | e";
|
||||
char* stages[shellMaxPipelineStages];
|
||||
/* shellMaxPipelineStages is 4, but this line has 5 stages -- must be
|
||||
rejected outright, leaving `text` completely untouched, exactly the
|
||||
same "malformed, nothing changed" contract shellExtractRedirects'
|
||||
own dangling-operator case already uses. */
|
||||
int count = shellSplitPipelineStages(text, stages, shellMaxPipelineStages);
|
||||
KT_EQ(t, count, -1);
|
||||
KT_STREQ(t, text, "a | b | c | d | e");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user