shell32/tests: Write proper tests for CommandLineToArgvW().

Francois Gouget fgouget at codeweavers.com
Tue Oct 2 10:41:45 CDT 2012


---

The existing tests were unreadable and very incomplete. I moved the 
tests around because I expect this infrastructure will be partly reused 
for test_argify().

 dlls/kernel32/tests/process.c |   12 --
 dlls/shell32/tests/shlexec.c  |  300 ++++++++++++++++++++++++++++-------------
 2 files changed, 206 insertions(+), 106 deletions(-)

diff --git a/dlls/kernel32/tests/process.c b/dlls/kernel32/tests/process.c
index c191068..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 /* FIXME */
-    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 6cfb2b0..1243f46 100644
--- a/dlls/shell32/tests/shlexec.c
+++ b/dlls/shell32/tests/shlexec.c
@@ -1006,6 +1006,211 @@ static void test_lpFile_parsed(void)
 
 }
 
+typedef struct
+{
+    const char* cmd;
+    const char* args[10];
+    int todo;
+} cmdline_tests_t;
+
+static 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 same rules apply except the opening quote
+     * is added to the set of consecutive quotes to get the effective quotes
+     * count. In other words we have:
+     * 1+3n   quotes -> n quotes plus start of a quoted string
+     * 1+3n+1 quotes -> n quotes (plus an empty string from the remaining pair)
+     * 1+3n+2 quotes -> n+1 quotes
+     */
+    {"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 one ends the
+     *   executable path. Remember that backslashes don't work so quotes cannot
+     *   be escaped!
+     * - 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.
+     */
+    {"\"spaced exe\" \"next arg\"",
+     {"spaced exe", "next arg", NULL}, 0},
+
+    {"\"exe\"arg1 arg2",
+     {"exe", "arg1", "arg2", NULL}, 0x31},
+
+    {"\"spaced exe\\\"arg1 arg2",
+     {"spaced exe\\", "arg1", "arg2", NULL}, 0x11},
+
+    {"\\\"exe \"arg one\"",
+     {"\\\"exe", "arg one", NULL}, 0x10},
+
+    {"\\\"quote\"exe\\ arg1",
+     {"\\\"quote\"exe\\", "arg1", NULL}, 0x11},
+
+    {"\\\\\"exe arg1",
+     {"\\\\\"exe", "arg1", NULL}, 0x11},
+
+    {NULL, {NULL}, 0}
+};
+
+static void test_commandline2argv(void)
+{
+    const cmdline_tests_t* test;
+    WCHAR cmdW[MAX_PATH], argW[MAX_PATH];
+    LPWSTR *cl2a;
+    int cl2a_count;
+    DWORD le;
+
+    test = cmdline_tests;
+    while (test->cmd)
+    {
+        LPWSTR *argsW;
+        int i, count;
+
+        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;
+        }
+
+        count = 0;
+        while (test->args[count])
+            count++;
+        if ((test->todo & 0x1) == 0)
+            ok(cl2a_count == count, "%s: got %d arguments instead of %d\n", test->cmd, cl2a_count, count);
+        else todo_wine
+            ok(cl2a_count == count, "%s: got %d arguments instead of %d\n", test->cmd, cl2a_count, count);
+
+        for (i = 0; i < cl2a_count; 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: got arg[%d]=%s instead of '%s'\n", test->cmd, i, wine_dbgstr_w(*argsW), wine_dbgstr_w(argW));
+                else todo_wine
+                    ok(!lstrcmpW(*argsW,argW), "%s: got arg[%d]=%s instead of '%s'\n", test->cmd, i, wine_dbgstr_w(*argsW), test->args[i]);
+            }
+            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);
+        test++;
+    }
+
+    SetLastError(0xdeadbeef);
+    cl2a = CommandLineToArgvW(cmdW, NULL);
+    le = GetLastError();
+    ok(cl2a == NULL && le == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n", cl2a, le);
+
+    SetLastError(0xdeadbeef);
+    cl2a = CommandLineToArgvW(NULL, NULL);
+    le = GetLastError();
+    ok(cl2a == NULL && le == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n", cl2a, le);
+
+    *cmdW = 0;
+    cl2a = CommandLineToArgvW(cmdW, &cl2a_count);
+    ok(cl2a_count == 1, "expected 1 args, got %d\n", cl2a_count);
+    if (cl2a_count == 1)
+    {
+        GetModuleFileNameW(NULL, argW, sizeof(argW)/sizeof(*argW));
+        ok(!lstrcmpW(cl2a[0], argW), "wrong path to the current executable\n");
+    }
+    if (cl2a) LocalFree(cl2a);
+}
+
 static void test_argify(void)
 {
     char fileA[MAX_PATH];
@@ -2284,99 +2489,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];
@@ -2420,6 +2532,7 @@ START_TEST(shlexec)
 
     init_test();
 
+    test_commandline2argv();
     test_argify();
     test_lpFile_parsed();
     test_filename();
@@ -2430,7 +2543,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