[3/3] shell32/tests: Write proper tests for CommandLineToArgvW().
Francois Gouget
fgouget at codeweavers.com
Thu Oct 4 02:54:16 CDT 2012
---
dlls/kernel32/tests/process.c | 12 --
dlls/shell32/tests/shlexec.c | 326 +++++++++++++++++++++++++++++------------
2 files changed, 232 insertions(+), 106 deletions(-)
diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c
index c7ff0d7..2bdc42e 100644
--- a/dlls/kernel32/tests/process.c
+++ b/dlls/kernel32/tests/process.c
@@ -298,18 +298,6 @@ static void doChild(const char* file, const char* option)
childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
}
childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
-
-#if 0
- int argcW;
- WCHAR** argvW;
-
- /* this is part of shell32... and should be tested there */
- argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
- for (i = 0; i < argcW; i++)
- {
- childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
- }
-#endif
childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
/* output of environment (Ansi) */
diff --git a/dlls/shell32/tests/shlexec.c b/dlls/shell32/tests/shlexec.c
index 131a9e6..3a7b124 100644
--- a/dlls/shell32/tests/shlexec.c
+++ b/dlls/shell32/tests/shlexec.c
@@ -953,6 +953,237 @@ static void test_lpFile_parsed(void)
"%s failed: rc=%lu\n", shell_call, rc);
}
+typedef struct
+{
+ const char* cmd;
+ const char* args[11];
+ int todo;
+} cmdline_tests_t;
+
+static const cmdline_tests_t cmdline_tests[] =
+{
+ {"exe arg1 arg2 \"arg three\" 'four five` six\\ $even)",
+ {"exe", "arg1", "arg2", "arg three", "'four", "five`", "six\\", "$even)", NULL}, 0},
+
+ {"exe arg=1 arg-2 three\tfour\rfour\nfour ",
+ {"exe", "arg=1", "arg-2", "three", "four\rfour\nfour", NULL}, 0},
+
+ {"exe arg\"one\" \"second\"arg thirdarg ",
+ {"exe", "argone", "secondarg", "thirdarg", NULL}, 0},
+
+ /* cmd's metacharacters have no special meaning */
+ {"exe \"one^\" \"arg\"&two three|four",
+ {"exe", "one^", "arg&two", "three|four", NULL}, 0},
+
+ /* Environment variables are not interpreted either */
+ {"exe %TMPDIR% %2",
+ {"exe", "%TMPDIR%", "%2", NULL}, 0},
+
+ /* If not followed by a quote, backslashes go through as is */
+ {"exe o\\ne t\\\\wo t\\\\\\ree f\\\\\\\\our ",
+ {"exe", "o\\ne", "t\\\\wo", "t\\\\\\ree", "f\\\\\\\\our", NULL}, 0},
+
+ {"exe \"o\\ne\" \"t\\\\wo\" \"t\\\\\\ree\" \"f\\\\\\\\our\" ",
+ {"exe", "o\\ne", "t\\\\wo", "t\\\\\\ree", "f\\\\\\\\our", NULL}, 0},
+
+ /* When followed by a quote their number is halved and the remainder
+ * escapes the quote
+ */
+ {"exe \\\"one \\\\\"two\" \\\\\\\"three \\\\\\\\\"four\" end",
+ {"exe", "\"one", "\\two", "\\\"three", "\\\\four", "end", NULL}, 0},
+
+ {"exe \"one\\\" still\" \"two\\\\\" \"three\\\\\\\" still\" \"four\\\\\\\\\" end",
+ {"exe", "one\" still", "two\\", "three\\\" still", "four\\\\", "end", NULL}, 0},
+
+ /* One can put a quote in an unquoted string by tripling it, that is in
+ * effect quoting it like so """ -> ". The general rule is as follows:
+ * 3n quotes -> n quotes
+ * 3n+1 quotes -> n quotes plus start of a quoted string
+ * 3n+2 quotes -> n quotes (plus an empty string from the remaining pair)
+ * Nicely, when n is 0 we get the standard rules back.
+ */
+ {"exe two\"\"quotes next",
+ {"exe", "twoquotes", "next", NULL}, 0},
+
+ {"exe three\"\"\"quotes next",
+ {"exe", "three\"quotes", "next", NULL}, 0x21},
+
+ {"exe four\"\"\"\" quotes\" next 4%3=1",
+ {"exe", "four\" quotes", "next", "4%3=1", NULL}, 0x61},
+
+ {"exe five\"\"\"\"\"quotes next",
+ {"exe", "five\"quotes", "next", NULL}, 0x21},
+
+ {"exe six\"\"\"\"\"\"quotes next",
+ {"exe", "six\"\"quotes", "next", NULL}, 0x20},
+
+ {"exe seven\"\"\"\"\"\"\" quotes\" next 7%3=1",
+ {"exe", "seven\"\" quotes", "next", "7%3=1", NULL}, 0x20},
+
+ {"exe twelve\"\"\"\"\"\"\"\"\"\"\"\"quotes next",
+ {"exe", "twelve\"\"\"\"quotes", "next", NULL}, 0x20},
+
+ {"exe thirteen\"\"\"\"\"\"\"\"\"\"\"\"\" quotes\" next 13%3=1",
+ {"exe", "thirteen\"\"\"\" quotes", "next", "13%3=1", NULL}, 0x20},
+
+ /* Inside a quoted string the opening quote is added to the set of
+ * consecutive quotes to get the effective quotes count. This gives:
+ * 1+3n quotes -> n quotes
+ * 1+3n+1 quotes -> n quotes plus closes the quoted string
+ * 1+3n+2 quotes -> n+1 quotes plus closes the quoted string
+ */
+ {"exe \"two\"\"quotes next",
+ {"exe", "two\"quotes", "next", NULL}, 0x21},
+
+ {"exe \"two\"\" next",
+ {"exe", "two\"", "next", NULL}, 0x21},
+
+ {"exe \"three\"\"\" quotes\" next 4%3=1",
+ {"exe", "three\" quotes", "next", "4%3=1", NULL}, 0x61},
+
+ {"exe \"four\"\"\"\"quotes next",
+ {"exe", "four\"quotes", "next", NULL}, 0x21},
+
+ {"exe \"five\"\"\"\"\"quotes next",
+ {"exe", "five\"\"quotes", "next", NULL}, 0x20},
+
+ {"exe \"six\"\"\"\"\"\" quotes\" next 7%3=1",
+ {"exe", "six\"\" quotes", "next", "7%3=1", NULL}, 0x20},
+
+ {"exe \"eleven\"\"\"\"\"\"\"\"\"\"\"quotes next",
+ {"exe", "eleven\"\"\"\"quotes", "next", NULL}, 0x20},
+
+ {"exe \"twelve\"\"\"\"\"\"\"\"\"\"\"\" quotes\" next 13%3=1",
+ {"exe", "twelve\"\"\"\" quotes", "next", "13%3=1", NULL}, 0x20},
+
+ /* The executable path has its own rules!!!
+ * - Backslashes have no special meaning.
+ * - If the first character is a quote, then the second quote ends the
+ * executable path.
+ * - The previous rule holds even if the next character is not a space!
+ * - If the first character is not a quote, then quotes have no special
+ * meaning either and the executable path stops at the first space.
+ * - The consecutive quotes rules don't apply either.
+ * - Even if there is no space between the executable path and the first
+ * argument, the latter is parsed using the regular rules.
+ */
+ {"exe\"file\"path arg1",
+ {"exe\"file\"path", "arg1", NULL}, 0x30},
+
+ {"exe\"path\\ arg1",
+ {"exe\"path\\", "arg1", NULL}, 0x31},
+
+ {"\\\"exe \"arg one\"",
+ {"\\\"exe", "arg one", NULL}, 0x10},
+
+ {"\"spaced exe\" \"next arg\"",
+ {"spaced exe", "next arg", NULL}, 0},
+
+ {"\"exe\"arg\" one\" argtwo",
+ {"exe", "arg one", "argtwo", NULL}, 0x31},
+
+ {"\"spaced exe\\\"arg1 arg2",
+ {"spaced exe\\", "arg1", "arg2", NULL}, 0x11},
+
+ {"\"two\"\" arg1 ",
+ {"two", " arg1 ", NULL}, 0x21},
+
+ {"\"three\"\"\" arg2",
+ {"three", "", "arg2", NULL}, 0x61},
+
+ {"\"four\"\"\"\"arg1",
+ {"four", "\"arg1", NULL}, 0x21},
+
+ /* If the first character is a space then the executable path is empty */
+ {" \"arg\"one argtwo",
+ {"", "argone", "argtwo", NULL}, 0},
+
+ {NULL, {NULL}, 0}
+};
+
+static BOOL test_one_cmdline(const cmdline_tests_t* test)
+{
+ WCHAR cmdW[MAX_PATH], argW[MAX_PATH];
+ LPWSTR *cl2a;
+ int cl2a_count;
+ LPWSTR *argsW;
+ int i, count;
+
+ /* trace("----- cmd='%s'\n", test->cmd); */
+ MultiByteToWideChar(CP_ACP, 0, test->cmd, -1, cmdW, sizeof(cmdW)/sizeof(*cmdW));
+ argsW = cl2a = CommandLineToArgvW(cmdW, &cl2a_count);
+ if (argsW == NULL && cl2a_count == -1)
+ {
+ win_skip("CommandLineToArgvW not implemented, skipping\n");
+ return FALSE;
+ }
+
+ count = 0;
+ while (test->args[count])
+ count++;
+ if ((test->todo & 0x1) == 0)
+ ok(cl2a_count == count, "%s: expected %d arguments, but got %d\n", test->cmd, count, cl2a_count);
+ else todo_wine
+ ok(cl2a_count == count, "%s: expected %d arguments, but got %d\n", test->cmd, count, cl2a_count);
+
+ for (i = 0; i < cl2a_count - 1; i++)
+ {
+ if (test->args[i])
+ {
+ MultiByteToWideChar(CP_ACP, 0, test->args[i], -1, argW, sizeof(argW)/sizeof(*argW));
+ if ((test->todo & (1 << (i+4))) == 0)
+ ok(!lstrcmpW(*argsW, argW), "%s: arg[%d] expected %s but got %s\n", test->cmd, i, wine_dbgstr_w(argW), wine_dbgstr_w(*argsW));
+ else todo_wine
+ ok(!lstrcmpW(*argsW, argW), "%s: arg[%d] expected %s but got %s\n", test->cmd, i, wine_dbgstr_w(argW), wine_dbgstr_w(*argsW));
+ }
+ else if ((test->todo & 0x1) == 0)
+ ok(0, "%s: got extra arg[%d]=%s\n", test->cmd, i, wine_dbgstr_w(*argsW));
+ else todo_wine
+ ok(0, "%s: got extra arg[%d]=%s\n", test->cmd, i, wine_dbgstr_w(*argsW));
+ argsW++;
+ }
+ LocalFree(cl2a);
+ return TRUE;
+}
+
+static void test_commandline2argv(void)
+{
+ static const WCHAR exeW[] = {'e','x','e',0};
+ const cmdline_tests_t* test;
+ WCHAR strW[MAX_PATH];
+ LPWSTR *args;
+ int numargs;
+ DWORD le;
+
+ test = cmdline_tests;
+ while (test->cmd)
+ {
+ if (!test_one_cmdline(test))
+ return;
+ test++;
+ }
+
+ SetLastError(0xdeadbeef);
+ args = CommandLineToArgvW(exeW, NULL);
+ le = GetLastError();
+ ok(args == NULL && le == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n", args, le);
+
+ SetLastError(0xdeadbeef);
+ args = CommandLineToArgvW(NULL, NULL);
+ le = GetLastError();
+ ok(args == NULL && le == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n", args, le);
+
+ *strW = 0;
+ args = CommandLineToArgvW(strW, &numargs);
+ ok(numargs == 1, "expected 1 args, got %d\n", numargs);
+ if (numargs == 1)
+ {
+ GetModuleFileNameW(NULL, strW, sizeof(strW)/sizeof(*strW));
+ ok(!lstrcmpW(args[0], strW), "wrong path to the current executable: %s instead of %s\n", wine_dbgstr_w(args[0]), wine_dbgstr_w(strW));
+ }
+ if (args) LocalFree(args);
+}
+
static void test_argify(void)
{
char fileA[MAX_PATH];
@@ -2210,99 +2441,6 @@ static void cleanup_test(void)
CoUninitialize();
}
-static void test_commandline(void)
-{
- static const WCHAR one[] = {'o','n','e',0};
- static const WCHAR two[] = {'t','w','o',0};
- static const WCHAR three[] = {'t','h','r','e','e',0};
- static const WCHAR four[] = {'f','o','u','r',0};
-
- static const WCHAR fmt1[] = {'%','s',' ','%','s',' ','%','s',' ','%','s',0};
- static const WCHAR fmt2[] = {' ','%','s',' ','%','s',' ','%','s',' ','%','s',0};
- static const WCHAR fmt3[] = {'%','s','=','%','s',' ','%','s','=','\"','%','s','\"',0};
- static const WCHAR fmt4[] = {'\"','%','s','\"',' ','\"','%','s',' ','%','s','\"',' ','%','s',0};
- static const WCHAR fmt5[] = {'\\','\"','%','s','\"',' ','%','s','=','\"','%','s','\\','\"',' ','\"','%','s','\\','\"',0};
- static const WCHAR fmt6[] = {0};
-
- static const WCHAR chkfmt1[] = {'%','s','=','%','s',0};
- static const WCHAR chkfmt2[] = {'%','s',' ','%','s',0};
- static const WCHAR chkfmt3[] = {'\\','\"','%','s','\"',0};
- static const WCHAR chkfmt4[] = {'%','s','=','%','s','\"',' ','%','s','\"',0};
- WCHAR cmdline[255];
- LPWSTR *args = (LPWSTR*)0xdeadcafe, pbuf;
- INT numargs = -1;
- size_t buflen;
- DWORD lerror;
-
- wsprintfW(cmdline,fmt1,one,two,three,four);
- args=CommandLineToArgvW(cmdline,&numargs);
- if (args == NULL && numargs == -1)
- {
- win_skip("CommandLineToArgvW not implemented, skipping\n");
- return;
- }
- ok(numargs == 4, "expected 4 args, got %i\n",numargs);
- ok(lstrcmpW(args[0],one)==0,"arg0 is not as expected\n");
- ok(lstrcmpW(args[1],two)==0,"arg1 is not as expected\n");
- ok(lstrcmpW(args[2],three)==0,"arg2 is not as expected\n");
- ok(lstrcmpW(args[3],four)==0,"arg3 is not as expected\n");
-
- SetLastError(0xdeadbeef);
- args=CommandLineToArgvW(cmdline,NULL);
- lerror=GetLastError();
- ok(args == NULL && lerror == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n",args,lerror);
- SetLastError(0xdeadbeef);
- args=CommandLineToArgvW(NULL,NULL);
- lerror=GetLastError();
- ok(args == NULL && lerror == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n",args,lerror);
-
- wsprintfW(cmdline,fmt2,one,two,three,four);
- args=CommandLineToArgvW(cmdline,&numargs);
- ok(numargs == 5, "expected 5 args, got %i\n",numargs);
- ok(args[0][0]==0,"arg0 is not as expected\n");
- ok(lstrcmpW(args[1],one)==0,"arg1 is not as expected\n");
- ok(lstrcmpW(args[2],two)==0,"arg2 is not as expected\n");
- ok(lstrcmpW(args[3],three)==0,"arg3 is not as expected\n");
- ok(lstrcmpW(args[4],four)==0,"arg4 is not as expected\n");
-
- wsprintfW(cmdline,fmt3,one,two,three,four);
- args=CommandLineToArgvW(cmdline,&numargs);
- ok(numargs == 2, "expected 2 args, got %i\n",numargs);
- wsprintfW(cmdline,chkfmt1,one,two);
- ok(lstrcmpW(args[0],cmdline)==0,"arg0 is not as expected\n");
- wsprintfW(cmdline,chkfmt1,three,four);
- ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n");
-
- wsprintfW(cmdline,fmt4,one,two,three,four);
- args=CommandLineToArgvW(cmdline,&numargs);
- ok(numargs == 3, "expected 3 args, got %i\n",numargs);
- ok(lstrcmpW(args[0],one)==0,"arg0 is not as expected\n");
- wsprintfW(cmdline,chkfmt2,two,three);
- ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n");
- ok(lstrcmpW(args[2],four)==0,"arg2 is not as expected\n");
-
- wsprintfW(cmdline,fmt5,one,two,three,four);
- args=CommandLineToArgvW(cmdline,&numargs);
- ok(numargs == 2, "expected 2 args, got %i\n",numargs);
- wsprintfW(cmdline,chkfmt3,one);
- todo_wine ok(lstrcmpW(args[0],cmdline)==0,"arg0 is not as expected\n");
- wsprintfW(cmdline,chkfmt4,two,three,four);
- todo_wine ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n");
-
- wsprintfW(cmdline,fmt6);
- args=CommandLineToArgvW(cmdline,&numargs);
- ok(numargs == 1, "expected 1 args, got %i\n",numargs);
- if (numargs == 1) {
- buflen = max(lstrlenW(args[0])+1,256);
- pbuf = HeapAlloc(GetProcessHeap(), 0, buflen*sizeof(pbuf[0]));
- GetModuleFileNameW(NULL, pbuf, buflen);
- pbuf[buflen-1] = 0;
- /* check args[0] is module file name */
- ok(lstrcmpW(args[0],pbuf)==0, "wrong path to the current executable\n");
- HeapFree(GetProcessHeap(), 0, pbuf);
- }
-}
-
static void test_directory(void)
{
char path[MAX_PATH], newdir[MAX_PATH];
@@ -2346,6 +2484,7 @@ START_TEST(shlexec)
init_test();
+ test_commandline2argv();
test_argify();
test_lpFile_parsed();
test_filename();
@@ -2356,7 +2495,6 @@ START_TEST(shlexec)
test_exes_long();
test_dde();
test_dde_default_app();
- test_commandline();
test_directory();
cleanup_test();
--
1.7.10.4
More information about the wine-patches
mailing list