mirror of
https://github.com/reactos/wine.git
synced 2024-11-29 22:50:43 +00:00
shell32/tests: Write proper tests for CommandLineToArgvW().
This commit is contained in:
parent
da6b02ceb1
commit
9ec7ab3fa0
@ -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, "argvA%d=%s\n", i, encodeA(myARGV[i]));
|
||||||
}
|
}
|
||||||
childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
|
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()));
|
childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
|
||||||
|
|
||||||
/* output of environment (Ansi) */
|
/* output of environment (Ansi) */
|
||||||
|
@ -953,6 +953,237 @@ static void test_lpFile_parsed(void)
|
|||||||
"%s failed: rc=%lu\n", shell_call, rc);
|
"%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)
|
static void test_argify(void)
|
||||||
{
|
{
|
||||||
char fileA[MAX_PATH];
|
char fileA[MAX_PATH];
|
||||||
@ -2210,99 +2441,6 @@ static void cleanup_test(void)
|
|||||||
CoUninitialize();
|
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)
|
static void test_directory(void)
|
||||||
{
|
{
|
||||||
char path[MAX_PATH], newdir[MAX_PATH];
|
char path[MAX_PATH], newdir[MAX_PATH];
|
||||||
@ -2346,6 +2484,7 @@ START_TEST(shlexec)
|
|||||||
|
|
||||||
init_test();
|
init_test();
|
||||||
|
|
||||||
|
test_commandline2argv();
|
||||||
test_argify();
|
test_argify();
|
||||||
test_lpFile_parsed();
|
test_lpFile_parsed();
|
||||||
test_filename();
|
test_filename();
|
||||||
@ -2356,7 +2495,6 @@ START_TEST(shlexec)
|
|||||||
test_exes_long();
|
test_exes_long();
|
||||||
test_dde();
|
test_dde();
|
||||||
test_dde_default_app();
|
test_dde_default_app();
|
||||||
test_commandline();
|
|
||||||
test_directory();
|
test_directory();
|
||||||
|
|
||||||
cleanup_test();
|
cleanup_test();
|
||||||
|
Loading…
Reference in New Issue
Block a user