From c5f5c74a375f8437a0d1d4db6713282fafbf9720 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 11 Sep 2026 20:50:48 +0200 Subject: [PATCH] test: add more cases for shellTest --- src/shellTest.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/src/shellTest.c b/src/shellTest.c index 5aafe27..2dc2f89 100644 --- a/src/shellTest.c +++ b/src/shellTest.c @@ -307,3 +307,160 @@ KTEST(shellExtractRedirectsOutputAppend) KT_TRUE(t, redirect.appendOut); } +KTEST(shellExtractRedirectsInputAndOutputTogetherKeepRealArgs) +{ + char text[] = "sort -r < in.txt > 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, 2); + KT_STREQ(t, argv[0], "sort"); + KT_STREQ(t, argv[1], "-r"); + KT_STREQ(t, redirect.inPath, "in.txt"); + KT_STREQ(t, redirect.outPath, "out.txt"); + KT_FALSE(t, redirect.appendOut); +} + +KTEST(shellExtractRedirectsOperatorsCanComeBeforeRealArgs) +{ + /* A real shell accepts redirection operators anywhere on the line, not + only trailing -- "> out.txt cat file.txt" is a weird but legal way + to write "cat file.txt > out.txt". */ + char text[] = "> out.txt 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_STREQ(t, redirect.outPath, "out.txt"); +} + +KTEST(shellExtractRedirectsLastOutputOperatorWins) +{ + char text[] = "cat > a.txt >> b.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], "cat"); + KT_STREQ(t, redirect.outPath, "b.txt"); + KT_TRUE(t, redirect.appendOut); +} + +KTEST(shellExtractRedirectsDanglingOperatorFailsWithoutMutating) +{ + char text[] = "cat file.txt >"; + char* argv[8]; + int argc = shellTokenize(text, argv, 8); + int originalArgc = argc; + ShellRedirect redirect; + bool ok = shellExtractRedirects(argv, &argc, &redirect); + KT_FALSE(t, ok); + KT_EQ(t, argc, originalArgc); + KT_STREQ(t, argv[0], "cat"); + KT_STREQ(t, argv[1], "file.txt"); + KT_STREQ(t, argv[2], ">"); +} + +KTEST(shellExtractRedirectsNoTokensYieldsNoRedirects) +{ + char* argv[1]; + int argc = 0; + ShellRedirect redirect; + bool ok = shellExtractRedirects(argv, &argc, &redirect); + KT_TRUE(t, ok); + KT_EQ(t, argc, 0); + KT_NULL(t, redirect.inPath); + KT_NULL(t, redirect.outPath); +} + +/* `2>`/`2>>` (stderr redirection) are a separate operator family from + `<`/`>`/`>>` above, targeting fd 2 instead of fd 0/fd 1 -- covered on + their own (truncate, append, combined with stdout, last-one-wins, + dangling) plus one case proving the two families never interfere with + each other even when both appear on the same line. */ + +KTEST(shellExtractRedirectsErrTruncate) +{ + char text[] = "prog 2> err.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], "prog"); + KT_NULL(t, redirect.inPath); + KT_NULL(t, redirect.outPath); + KT_NOTNULL(t, redirect.errPath); + KT_STREQ(t, redirect.errPath, "err.txt"); + KT_FALSE(t, redirect.appendErr); +} + +KTEST(shellExtractRedirectsErrAppend) +{ + char text[] = "prog 2>> err.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], "prog"); + KT_STREQ(t, redirect.errPath, "err.txt"); + KT_TRUE(t, redirect.appendErr); +} + +KTEST(shellExtractRedirectsStdoutAndStderrTogetherKeepRealArgs) +{ + char text[] = "sort -r > out.txt 2> err.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], "sort"); + KT_STREQ(t, argv[1], "-r"); + KT_STREQ(t, redirect.outPath, "out.txt"); + KT_FALSE(t, redirect.appendOut); + KT_STREQ(t, redirect.errPath, "err.txt"); + KT_FALSE(t, redirect.appendErr); +} + +KTEST(shellExtractRedirectsLastErrOperatorWins) +{ + char text[] = "cat 2> a.txt 2>> b.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], "cat"); + KT_STREQ(t, redirect.errPath, "b.txt"); + KT_TRUE(t, redirect.appendErr); +} + +KTEST(shellExtractRedirectsDanglingErrOperatorFailsWithoutMutating) +{ + char text[] = "cat file.txt 2>"; + char* argv[8]; + int argc = shellTokenize(text, argv, 8); + int originalArgc = argc; + ShellRedirect redirect; + bool ok = shellExtractRedirects(argv, &argc, &redirect); + KT_FALSE(t, ok); + KT_EQ(t, argc, originalArgc); + KT_STREQ(t, argv[0], "cat"); + KT_STREQ(t, argv[1], "file.txt"); + KT_STREQ(t, argv[2], "2>"); +} +