diff --git a/configure b/configure index e331f1457eb513343a3c1df53095ce28bd572e69..1d884cc8bb3e5322c4206c5b01c6526a5b0deb42 100755 --- a/configure +++ b/configure @@ -19826,6 +19826,26 @@ ALL_MAKEFILE_DEPENDS="$ALL_MAKEFILE_DEPENDS programs/cmd/Makefile: programs/cmd/Makefile.in programs/Makeprog.rules config.status" ac_config_files="$ac_config_files programs/cmd/Makefile" +ALL_MAKEFILE_DEPENDS="$ALL_MAKEFILE_DEPENDS +programs/cmd/tests: __builddeps__ +programs/cmd/tests/__crosstest__: __buildcrossdeps__" +ALL_TEST_BINARIES="$ALL_TEST_BINARIES \\ + cmd.exe_test.exe" +ALL_WINETEST_DEPENDS="$ALL_WINETEST_DEPENDS +cmd.exe_test.exe: \$(TOPOBJDIR)/programs/cmd/tests/cmd.exe_test.exe$DLLEXT + cp \$(TOPOBJDIR)/programs/cmd/tests/cmd.exe_test.exe$DLLEXT \$@ && \$(STRIP) \$@ +cmd.exe_test.rc: + echo \"cmd.exe_test.exe TESTRES \\\"cmd.exe_test.exe\\\"\" >\$@ || (\$(RM) \$@ && false) +cmd.exe_test.res: cmd.exe_test.rc cmd.exe_test.exe" +ALL_MAKEFILES="$ALL_MAKEFILES \\ + programs/cmd/tests/Makefile" +test "x$enable_tests" != xno && ALL_TEST_DIRS="$ALL_TEST_DIRS \\ + programs/cmd/tests" +ALL_MAKEFILE_DEPENDS="$ALL_MAKEFILE_DEPENDS +\$(RECURSE_TARGETS:%=programs/cmd/tests/%) programs/cmd/tests: programs/cmd/tests/Makefile \$(MAKEDEP) +programs/cmd/tests/Makefile: programs/cmd/tests/Makefile.in Maketest.rules config.status" +ac_config_files="$ac_config_files programs/cmd/tests/Makefile" + ALL_MAKEFILES="$ALL_MAKEFILES \\ programs/cmdlgtst/Makefile" test "x$enable_cmdlgtst" != xno && ALL_PROGRAM_DIRS="$ALL_PROGRAM_DIRS \\ @@ -21559,6 +21579,7 @@ do "programs/cacls/Makefile") CONFIG_FILES="$CONFIG_FILES programs/cacls/Makefile" ;; "programs/clock/Makefile") CONFIG_FILES="$CONFIG_FILES programs/clock/Makefile" ;; "programs/cmd/Makefile") CONFIG_FILES="$CONFIG_FILES programs/cmd/Makefile" ;; + "programs/cmd/tests/Makefile") CONFIG_FILES="$CONFIG_FILES programs/cmd/tests/Makefile" ;; "programs/cmdlgtst/Makefile") CONFIG_FILES="$CONFIG_FILES programs/cmdlgtst/Makefile" ;; "programs/control/Makefile") CONFIG_FILES="$CONFIG_FILES programs/control/Makefile" ;; "programs/dxdiag/Makefile") CONFIG_FILES="$CONFIG_FILES programs/dxdiag/Makefile" ;; diff --git a/configure.ac b/configure.ac index b750168afa8bb61d9ac4a94460dd2ba243756355..f5845e1a839d4b667e54f0131a76e98b149d589f 100644 --- a/configure.ac +++ b/configure.ac @@ -2619,6 +2619,7 @@ WINE_CONFIG_MAKEFILE([programs/Makefile],[Make.rules],[],[ALL_TOP_DIRS]) WINE_CONFIG_PROGRAM(cacls,[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS]) WINE_CONFIG_PROGRAM(clock,[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS]) WINE_CONFIG_PROGRAM(cmd,[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS]) +WINE_CONFIG_TEST(programs/cmd/tests) WINE_CONFIG_PROGRAM(cmdlgtst,[ALL_PROGRAM_DIRS]) WINE_CONFIG_PROGRAM(control,[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS]) WINE_CONFIG_PROGRAM(dxdiag,[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS]) diff --git a/programs/cmd/tests/Makefile.in b/programs/cmd/tests/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..b10f7eb0a0a436ca59db1116a3781e9aa28402ad --- /dev/null +++ b/programs/cmd/tests/Makefile.in @@ -0,0 +1,14 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +TESTDLL = cmd.exe +IMPORTS = kernel32 + +CTESTS = batch.c + +RC_SRCS = rsrc.rc + +@MAKE_TEST_RULES@ + +@DEPENDENCIES@ # everything below this line is overwritten by make depend diff --git a/programs/cmd/tests/batch.c b/programs/cmd/tests/batch.c new file mode 100644 index 0000000000000000000000000000000000000000..1d822a154862b347d66953427e21ab53b21fff14 --- /dev/null +++ b/programs/cmd/tests/batch.c @@ -0,0 +1,281 @@ +/* + * Copyright 2009 Dan Kegel + * Copyright 2010 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windows.h> +#include <stdio.h> + +#include "wine/test.h" + +static char workdir[MAX_PATH]; +static DWORD workdir_len; + +static BOOL run_cmd(const char *cmd_data, DWORD cmd_size) +{ + SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE}; + char command[] = "test.cmd"; + STARTUPINFOA si = {sizeof(si)}; + PROCESS_INFORMATION pi; + HANDLE file; + DWORD size; + BOOL bres; + + file = CreateFileA("test.cmd", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n"); + if(file == INVALID_HANDLE_VALUE) + return FALSE; + + bres = WriteFile(file, cmd_data, cmd_size, &size, NULL); + CloseHandle(file); + ok(bres, "Could not write to file: %u\n", GetLastError()); + if(!bres) + return FALSE; + + file = CreateFileA("test.out", GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, &sa, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n"); + if(file == INVALID_HANDLE_VALUE) + return FALSE; + + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdOutput = file; + bres = CreateProcessA(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); + ok(bres, "CreateProcess failed: %u\n", GetLastError()); + if(!bres) { + DeleteFileA("test.out"); + return FALSE; + } + + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + CloseHandle(file); + DeleteFileA("test.cmd"); + return TRUE; +} + +static DWORD map_file(const char *file_name, const char **ret) +{ + HANDLE file, map; + DWORD size; + + file = CreateFileA(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed: %08x\n", GetLastError()); + if(file == INVALID_HANDLE_VALUE) + return 0; + + size = GetFileSize(file, NULL); + + map = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL); + CloseHandle(file); + ok(map != NULL, "CreateFileMapping(%s) failed: %u\n", file_name, GetLastError()); + if(!map) + return 0; + + *ret = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); + ok(*ret != NULL, "MapViewOfFile failed: %u\n", GetLastError()); + CloseHandle(map); + if(!*ret) + return 0; + + return size; +} + +static const char *compare_line(const char *out_line, const char *out_end, const char *exp_line, + const char *exp_end) +{ + const char *out_ptr = out_line, *exp_ptr = exp_line; + + static const char pwd_cmd[] = {'@','p','w','d','@'}; + static const char todo_space_cmd[] = {'@','t','o','d','o','_','s','p','a','c','e','@'}; + + while(exp_ptr < exp_end) { + if(*exp_ptr == '@') { + if(exp_ptr+sizeof(pwd_cmd) <= exp_end + && !memcmp(exp_ptr, pwd_cmd, sizeof(pwd_cmd))) { + exp_ptr += sizeof(pwd_cmd); + if(out_end-out_ptr < workdir_len || memcmp(out_ptr, workdir, workdir_len)) + return out_ptr; + out_ptr += workdir_len; + continue; + }else if(exp_ptr+sizeof(todo_space_cmd) <= exp_end + && !memcmp(exp_ptr, todo_space_cmd, sizeof(todo_space_cmd))) { + exp_ptr += sizeof(todo_space_cmd); + todo_wine ok(*out_ptr == ' ', "expected space\n"); + if(out_ptr < out_end && *out_ptr == ' ') + out_ptr++; + continue; + } + } + if(out_ptr == out_end || *out_ptr != *exp_ptr++) + return out_ptr; + out_ptr++; + } + + return exp_ptr == exp_end ? NULL : out_ptr; +} + +static void test_output(const char *out_data, DWORD out_size, const char *exp_data, DWORD exp_size) +{ + const char *out_ptr = out_data, *exp_ptr = exp_data, *out_nl, *exp_nl, *err; + DWORD line = 0; + + while(out_ptr < out_data+out_size && exp_ptr < exp_data+exp_size) { + line++; + + for(exp_nl = exp_ptr; exp_nl < exp_data+exp_size && *exp_nl != '\r' && *exp_nl != '\n'; exp_nl++); + for(out_nl = out_ptr; out_nl < out_data+out_size && *out_nl != '\r' && *out_nl != '\n'; out_nl++); + + err = compare_line(out_ptr, out_nl, exp_ptr, exp_nl); + if(err == out_nl) + ok(0, "unexpected end of line %d (got '%.*s', wanted '%.*s')\n", + line, out_nl-out_ptr, out_ptr, exp_nl-exp_ptr, exp_ptr); + else if(err) + ok(0, "unexpected char 0x%x position %d in line %d (got '%.*s', wanted '%.*s')\n", + *err, err-out_ptr, line, out_nl-out_ptr, out_ptr, exp_nl-exp_ptr, exp_ptr); + + exp_ptr = exp_nl+1; + out_ptr = out_nl+1; + if(out_nl+1 < out_data+out_size && out_nl[0] == '\r' && out_nl[1] == '\n') + out_ptr++; + if(exp_nl+1 < exp_data+exp_size && exp_nl[0] == '\r' && exp_nl[1] == '\n') + exp_ptr++; + } + + ok(exp_ptr >= exp_data+exp_size, "unexpected end of output in line %d, expected 0x%x\n", line, *exp_ptr); + ok(out_ptr >= out_data+out_size, "too long output\n"); +} + +static void run_test(const char *cmd_data, DWORD cmd_size, const char *exp_data, DWORD exp_size) +{ + const char *out_data; + DWORD out_size; + + if(!run_cmd(cmd_data, cmd_size)) + return; + + out_size = map_file("test.out", &out_data); + if(out_size) { + test_output(out_data, out_size, exp_data, exp_size); + UnmapViewOfFile(out_data); + } + DeleteFileA("test.out"); +} + +static void run_from_file(char *file_name) +{ + char out_name[MAX_PATH]; + const char *test_data, *out_data; + DWORD test_size, out_size; + + test_size = map_file(file_name, &test_data); + if(!test_size) { + ok(0, "Could not map file %s: %u\n", file_name, GetLastError()); + return; + } + + sprintf(out_name, "%s.out", file_name); + out_size = map_file(out_name, &out_data); + if(!out_size) { + ok(0, "Could not map file %s: %u\n", out_name, GetLastError()); + UnmapViewOfFile(test_data); + return; + } + + run_test(test_data, test_size, out_data, out_size); + + UnmapViewOfFile(test_data); + UnmapViewOfFile(out_data); +} + +static DWORD load_resource(const char *name, const char *type, const char **ret) +{ + const char *res; + HRSRC src; + DWORD size; + + src = FindResourceA(NULL, name, type); + ok(src != NULL, "Could not find resource %s: %u\n", name, GetLastError()); + if(!src) + return 0; + + res = LoadResource(NULL, src); + size = SizeofResource(NULL, src); + while(size && !res[size-1]) + size--; + + *ret = res; + return size; +} + +static BOOL WINAPI test_enum_proc(HMODULE module, LPCTSTR type, LPSTR name, LONG_PTR param) +{ + const char *cmd_data, *out_data; + DWORD cmd_size, out_size; + char res_name[100]; + + trace("running %s test...\n", name); + + cmd_size = load_resource(name, type, &cmd_data); + if(!cmd_size) + return TRUE; + + sprintf(res_name, "%s.out", name); + out_size = load_resource(res_name, "TESTOUT", &out_data); + if(!out_size) + return TRUE; + + run_test(cmd_data, cmd_size, out_data, out_size); + return TRUE; +} + +static int cmd_available(void) +{ + STARTUPINFOA si; + PROCESS_INFORMATION pi; + char cmd[] = "cmd /c exit 0"; + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + if (CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return TRUE; + } + return FALSE; +} + +START_TEST(batch) +{ + int argc; + char **argv; + + if (!cmd_available()) { + win_skip("cmd not installed, skipping cmd tests\n"); + return; + } + + workdir_len = GetCurrentDirectoryA(sizeof(workdir), workdir); + + argc = winetest_get_mainargs(&argv); + if(argc > 2) + run_from_file(argv[2]); + else + EnumResourceNamesA(NULL, "TESTCMD", test_enum_proc, 0); +} diff --git a/programs/cmd/tests/rsrc.rc b/programs/cmd/tests/rsrc.rc new file mode 100644 index 0000000000000000000000000000000000000000..791edb39842198e1936a1ea43f4b82eed3985098 --- /dev/null +++ b/programs/cmd/tests/rsrc.rc @@ -0,0 +1,23 @@ +/* + * Copyright 2010 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: test_echo.cmd */ +test_echo.cmd TESTCMD "test_echo.cmd" + +/* @makedep: test_echo.cmd.out */ +test_echo.cmd.out TESTOUT "test_echo.cmd.out" diff --git a/programs/cmd/tests/test_echo.cmd b/programs/cmd/tests/test_echo.cmd new file mode 100644 index 0000000000000000000000000000000000000000..611853e909245ccc760382a6731e9429323a28a9 --- /dev/null +++ b/programs/cmd/tests/test_echo.cmd @@ -0,0 +1,9 @@ +rem Tests for echo + +echo word +echo 'singlequotedword' +echo "doublequotedword" +@echo at-echoed-word +echo "/?" +echo. +echo . diff --git a/programs/cmd/tests/test_echo.cmd.out b/programs/cmd/tests/test_echo.cmd.out new file mode 100644 index 0000000000000000000000000000000000000000..cebfc337a6ef595aa7cd098af87b9b237f04ba36 --- /dev/null +++ b/programs/cmd/tests/test_echo.cmd.out @@ -0,0 +1,21 @@ + +@pwd@>rem Tests for echo@todo_space@ + +@pwd@>echo word@todo_space@ +word + +@pwd@>echo 'singlequotedword'@todo_space@ +'singlequotedword' + +@pwd@>echo "doublequotedword"@todo_space@ +"doublequotedword" +at-echoed-word + +@pwd@>echo "/?"@todo_space@ +"/?" + +@pwd@>echo. + + +@pwd@>echo .@todo_space@ +.