From: Eric Pouech epouech@codeweavers.com
Native CMD.EXE has different behavior when running .BAT or .CMD file for some builtin commands.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/batch.c | 37 ++- programs/cmd/tests/rsrc.rc | 6 + programs/cmd/tests/test_builtins.bat | 319 +++++++++++++++++++++++ programs/cmd/tests/test_builtins.bat.exp | 223 ++++++++++++++++ programs/cmd/tests/test_builtins.cmd | 2 + 5 files changed, 577 insertions(+), 10 deletions(-) create mode 100644 programs/cmd/tests/test_builtins.bat create mode 100644 programs/cmd/tests/test_builtins.bat.exp
diff --git a/programs/cmd/tests/batch.c b/programs/cmd/tests/batch.c index d44d2d33dec..6bc5e916ea9 100644 --- a/programs/cmd/tests/batch.c +++ b/programs/cmd/tests/batch.c @@ -74,17 +74,21 @@ static const char* convert_input_data(const char *data, DWORD size, DWORD *new_s return new_data; }
-static BOOL run_cmd(const char *cmd_data, DWORD cmd_size) +static BOOL run_cmd(const char *res_name, const char *cmd_data, DWORD cmd_size) { SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE}; - char command[] = "test.cmd"; + char command_cmd[] = "test.cmd", command_bat[] = "test.bat"; + char *command; STARTUPINFOA si = {sizeof(si)}; PROCESS_INFORMATION pi; HANDLE file,fileerr; DWORD size; BOOL bres;
- file = CreateFileA("test.cmd", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + command = (strlen(res_name) >= 4 && !stricmp(res_name + strlen(res_name) - 4, ".cmd")) ? + command_cmd : command_bat; + + file = CreateFileA(command, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n"); if(file == INVALID_HANDLE_VALUE) @@ -123,7 +127,7 @@ static BOOL run_cmd(const char *cmd_data, DWORD cmd_size) CloseHandle(pi.hProcess); CloseHandle(file); CloseHandle(fileerr); - DeleteFileA("test.cmd"); + DeleteFileA(command); return TRUE; }
@@ -131,8 +135,21 @@ 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); + unsigned retries; + + /* Even if .cmd/.bat wait on child process succeeded, there are cases where the + * output file is not closed yet (on Windows) (seems to only happen with .bat input files). + * So retry a couple of times before failing. + * Note: using file share option works at once, but we cannot be sure all + * output has been flushed. + */ + for (retries = 0; retries < 50; retries++) + { + file = CreateFileA(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + if (file != INVALID_HANDLE_VALUE || GetLastError() != ERROR_SHARING_VIOLATION) + break; + Sleep(1); + } ok(file != INVALID_HANDLE_VALUE, "CreateFile failed: %08lx\n", GetLastError()); if(file == INVALID_HANDLE_VALUE) return 0; @@ -358,7 +375,7 @@ static void test_output(const char *out_data, DWORD out_size, const char *exp_da ok(out_ptr >= out_data+out_size, "too long output, got additional %s\n", out_ptr); }
-static void run_test(const char *cmd_data, DWORD cmd_size, const char *exp_data, DWORD exp_size) +static void run_test(const char *res_name, const char *cmd_data, DWORD cmd_size, const char *exp_data, DWORD exp_size) { const char *out_data, *actual_cmd_data; DWORD out_size, actual_cmd_size; @@ -367,7 +384,7 @@ static void run_test(const char *cmd_data, DWORD cmd_size, const char *exp_data, if(!actual_cmd_size || !actual_cmd_data) goto cleanup;
- if(!run_cmd(actual_cmd_data, actual_cmd_size)) + if(!run_cmd(res_name, actual_cmd_data, actual_cmd_size)) goto cleanup;
out_size = map_file("test.out", &out_data); @@ -402,7 +419,7 @@ static void run_from_file(const char *file_name) return; }
- run_test(test_data, test_size, out_data, out_size); + run_test(file_name, test_data, test_size, out_data, out_size);
UnmapViewOfFile(test_data); UnmapViewOfFile(out_data); @@ -445,7 +462,7 @@ static BOOL WINAPI test_enum_proc(HMODULE module, LPCSTR type, LPSTR name, LONG_ if(!out_size) return TRUE;
- run_test(cmd_data, cmd_size, out_data, out_size); + run_test(name, cmd_data, cmd_size, out_data, out_size); return TRUE; }
diff --git a/programs/cmd/tests/rsrc.rc b/programs/cmd/tests/rsrc.rc index affe04c2bdd..0336863963f 100644 --- a/programs/cmd/tests/rsrc.rc +++ b/programs/cmd/tests/rsrc.rc @@ -22,6 +22,12 @@ test_builtins.cmd TESTCMD "test_builtins.cmd" /* @makedep: test_builtins.cmd.exp */ test_builtins.cmd.exp TESTOUT "test_builtins.cmd.exp"
+/* @makedep: test_builtins.bat */ +test_builtins.bat TESTCMD "test_builtins.bat" + +/* @makedep: test_builtins.bat.exp */ +test_builtins.bat.exp TESTOUT "test_builtins.bat.exp" + /* @makedep: test_cmdline.cmd */ test_cmdline.cmd TESTCMD "test_cmdline.cmd"
diff --git a/programs/cmd/tests/test_builtins.bat b/programs/cmd/tests/test_builtins.bat new file mode 100644 index 00000000000..a95c94c5ad8 --- /dev/null +++ b/programs/cmd/tests/test_builtins.bat @@ -0,0 +1,319 @@ +echo Tests for cmd's builtin commands BAT +@echo off +setlocal EnableDelayedExpansion + +rem All the success/failure tests are meant to be duplicated in test_builtins.cmd +rem So be sure to update both files at once + +echo --- success/failure for basics +call :setError 0 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +call :setError 33 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +call :setError 666 & (echo foo &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (echo foo >> h:\i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & echo foo >> h:\i\dont\exist\at\all.txt & echo ERRORLEVEL !errorlevel! +echo --- success/failure for IF/FOR blocks +call :setError 666 & ((if 1==1 echo "">NUL) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((if 1==0 echo "">NUL) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((if 1==1 (call :setError 33)) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((if 1==0 (call :setError 33) else call :setError 34) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((for %%i in () do echo "") &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((for %%i in () do call :setError 33) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((for %%i in (a) do call :setError 0) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((for %%i in (a) do call :setError 33) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for external command +mkdir foo & cd foo +call :setError 666 & (I\dont\exist.exe &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & I\dont\exist.exe & echo ERRORLEVEL !errorlevel! +call :setError 666 & (Idontexist.exe &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & Idontexist.exe & echo ERRORLEVEL !errorlevel! +call :setError 666 & (cmd.exe /c "echo foo & exit /b 0" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (cmd.exe /c "echo foo & exit /b 1024" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (I\dont\exist.html &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem can't run this test, generates a nice popup under windows +rem echo:>foobar.IDontExist +rem call :setError 666 & (foobar.IDontExist &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo +echo --- success/failure for CALL command +mkdir foo & cd foo +echo exit /b %%1 > foobar.bat +rem call :setError 666 & (call I\dont\exist.exe &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem terminates batch exec on native... +call :setError 666 & (call Idontexist.exe &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call .\foobar.bat 0 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call .\foobar.bat 1024 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call cmd.exe /c "echo foo & exit /b 0" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call cmd.exe /c "echo foo & exit /b 1025" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call rmdir foobar.dir &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo +echo --- success/failure for pipes +call :setError 666 & ((echo a | echo b) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((echo a | call :setError 34) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((call :setError 33 | echo a) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((echo a | rmdir I\dont\exist\at\all) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((rmdir I\dont\exist\at\all | echo a) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem in a pipe, if LHS or RHS can't be started, the whole cmd is abandonned (not just the pipe!!) +echo ^( %%1 ^| %%2 ^) > foo.bat +echo echo AFTER %%ERRORLEVEL%% >> foo.bat +call :setError 666 & (cmd.exe /q /c "call foo.bat echo I\dont\exist.exe" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (cmd.exe /q /c "call foo.bat I\dont\exist.exe echo" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +erase /q foo.bat +echo --- success/failure for START command +call :setError 666 & (start "" /foobar >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem call :setError 666 & (start /B I\dont\exist.exe &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem can't run this test, generates a nice popup under windows +call :setError 666 & (start "" /B /WAIT cmd.exe /c "echo foo & exit /b 1024" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (start "" /B cmd.exe /c "(choice /C:YN /T:3 /D:Y > NUL) & exit /b 1024" &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for TYPE command +mkdir foo & cd foo +echo a > fileA +echo b > fileB +call :setError 666 & (type &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (type NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (type i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (type file* i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- +call :setError 666 & (type i\dont\exist\at\all.txt file* &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo + +echo --- success/failure for COPY command +mkdir foo & cd foo +echo a > fileA +echo b > fileB +call :setError 666 & (copy fileA >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (copy fileA fileZ >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (copy fileA fileZ /-Y >NUL <NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (copy fileA+fileD fileZ /-Y >NUL <NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (copy fileD+fileA fileZ /-Y >NUL <NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +if exist fileD echo Unexpected fileD +cd .. && rd /q /s foo + +echo --- success/failure for MOVE command +mkdir foo & cd foo +echo a > fileA +echo b > fileB +call :setError 666 & (move >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (move fileA fileC >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (move fileC nowhere\fileC >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (move fileD fileE >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (move fileC fileB /-Y >NUL <NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo + +echo --- success/failure for RENAME command +mkdir foo & cd foo +echo a > fileA +echo b > fileB +call :setError 666 & (rename fileB >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (rename fileB fileA >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (rename fileB nowhere\fileB >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (rename fileD fileC >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (rename fileB fileC >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo + +echo --- success/failure for ERASE command +mkdir foo & cd foo +echo a > fileA +echo b > fileB +echo e > fileE +call :setError 666 & (erase &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (erase fileE &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (erase i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (erase file* i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo + +echo --- success/failure for change drive command +pushd C:\ +call :setError 666 & (c: &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (1: &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call c: &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (call 1: &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +popd + +echo --- success/failure for MKDIR,MD command +mkdir foo & cd foo +call :setError 666 & (mkdir &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mkdir abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mkdir abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mkdir @:\cba\abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mkdir NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo + +echo --- success/failure for CD command +mkdir foo & cd foo +mkdir abc +call :setError 666 & (cd abc >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (cd abc >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (cd .. >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (cd >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo + +echo --- success/failure for PUSHD/POPD commands +mkdir foo & cd foo +mkdir abc +call :setError 666 & (pushd &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (pushd abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (pushd abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (popd abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (popd &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & popd & echo ERRORLEVEL !errorlevel! +cd .. && rd /q /s foo + +echo --- success/failure for DIR command +mkdir foo & cd foo +echo a > fileA +echo b > fileB +mkdir dir +echo b > dir\fileB +call :setError 666 & (dir /e >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (dir zzz >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (dir fileA zzz >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (dir zzz fileA >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (dir dir\zzz >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (dir file* >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo +echo --- success/failure for RMDIR/RD command +mkdir foo & cd foo +mkdir abc +call :setError 666 & (rmdir &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo "">abc\abc +call :setError 666 & (rmdir abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (rmdir abc\abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +erase abc\abc +call :setError 666 & (rmdir abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (rmdir abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (rmdir @:\cba\abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo +mkdir foo & cd foo +mkdir abc +call :setError 666 & rmdir & echo ERRORLEVEL !errorlevel! +echo "">abc\abc +call :setError 666 & rmdir abc & echo ERRORLEVEL !errorlevel! +call :setError 666 & rmdir abc\abc & echo ERRORLEVEL !errorlevel! +erase abc\abc +call :setError 666 & rmdir abc & echo ERRORLEVEL !errorlevel! +call :setError 666 & rmdir abc & echo ERRORLEVEL !errorlevel! +call :setError 666 & rmdir @:\cba\abc & echo ERRORLEVEL !errorlevel! +cd .. && rd /q /s foo + +echo --- success/failure for MKLINK command +mkdir foo & cd foo +call :setError 666 & (mklink &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mklink /h foo &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mklink /h foo foo &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mklink /z foo foo &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo bar > foo +call :setError 666 & (mklink /h foo foo &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mklink /h bar foo >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (mklink /h bar foo &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +cd .. && rd /q /s foo + +echo --- success/failure for SETLOCAL/ENDLOCAL commands +call :setError 666 & (setlocal foobar &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (setlocal &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (endlocal foobar &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (setlocal DisableExtensions &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (setlocal EnableExtensions &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for DATE command +call :setError 666 & (date /t >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (date AAAA >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem need evelated priviledges to set the date +echo --- success/failure for TIME command +call :setError 666 & (time /t >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (time AAAA >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem need evelated priviledges to set the time +echo --- success/failure for BREAK command +call :setError 666 & (break &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (break 345 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for VER command +call :setError 666 & (ver >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (ver foo >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (ver /f >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for VERIFY command +call :setError 666 & (verify >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (verify on >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (verify foobar >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for VOL command +call :setError 666 & (vol >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (vol c: >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (vol foobar >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (vol /Z >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for LABEL command +call :setError 666 & (<NUL label >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem need evelated priviledges to test + +echo --- success/failure for PATH command +call :setError 666 & (path >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +set SAVED_PATH=%PATH% > NUL +call :setError 666 & (path @:\I\dont\Exist &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +path +call :setError 666 & (path ; &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +path +call :setError 666 & (path !SAVED_PATH! &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +set "SAVED_PATH=" +echo --- success/failure for SET command +call :setError 666 & (set >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (set "SAVED_PATH=%PATH%" >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (set S >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (set "SAVED_PATH=" >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (set "SAVED_PATH=" >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (set /Q >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (set ThisVariableLikelyDoesntExist >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem missing /A and /p tests +echo --- success/failure for ASSOC command +call :setError 666 & (assoc >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (assoc cmd >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (assoc .idontexist >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem testing changing the assoc requires elevated privilege +echo --- success/failure for FTYPE command +call :setError 666 & (ftype >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (ftype cmdfile >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (ftype fileidontexist >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem testing changing the ftype requires elevated privilege +echo --- success/failure for SHIFT command +call :setError 666 & shift /abc &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +call :testSuccessFailureShift 1 +goto :afterSuccessFailureShift +:testSuccessFailureShift +call :setError 666 & shift &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +call :setError 666 & shift &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +goto :eof +:afterSuccessFailureShift +echo --- success/failure for HELP command +call :setError 666 & help >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +call :setError 666 & help dir >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +call :setError 666 & help ACommandThatLikelyDoesntExist >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +echo --- success/failure for PROMPT command +call :setError 666 & prompt >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +rem doesn't seem to set errors either on invalid $ escapes, nor qualifiers + +echo --- success/failure for CLS command +call :setError 666 & (cls &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (cls foobar &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (cls /X &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for COLOR command +call :setError 666 & (color fc < NUL > NUL 2>&1 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem TODO: color is also hard to test: it requires fd 1 to be bound to a console, so we can't redirect its output +echo --- success/failure for TITLE command +call :setError 666 & (title a new title &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (title &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for CHOICE command +call :setError 666 & (choice <NUL >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (choice /c <NUL >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((echo A | choice /C:BA) >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (choice /C:BA <NUL >NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem syntax errors in command return INVALID_FUNCTION, need to find a test for returning 255 +echo --- success/failure for MORE command +call :setError 666 & (more NUL &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (more I\dont\exist.txt > NUL 2>&1 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (echo foo | more &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +echo --- success/failure for PAUSE command +call :setError 666 & (pause < NUL > NUL 2>&1 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +rem TODO: pause is harder to test when fd 1 is a console handle as we don't control output +echo --- +setlocal DisableDelayedExpansion + +goto :eof + +rem Subroutine to set errorlevel and return +:setError +exit /B %1 +rem This line runs under cmd in windows NT 4, but not in more modern versions. diff --git a/programs/cmd/tests/test_builtins.bat.exp b/programs/cmd/tests/test_builtins.bat.exp new file mode 100644 index 00000000000..12dd22effed --- /dev/null +++ b/programs/cmd/tests/test_builtins.bat.exp @@ -0,0 +1,223 @@ + +@pwd@>echo Tests for cmd's builtin commands BAT@space@ +Tests for cmd's builtin commands BAT +--- success/failure for basics +SUCCESS 0 +FAILURE 33 +foo@space@ +SUCCESS 666 +FAILURE 1 +ERRORLEVEL 666 +--- success/failure for IF/FOR blocks +SUCCESS 666 +SUCCESS 666 +FAILURE 33 +FAILURE 34 +SUCCESS 666 +SUCCESS 666 +SUCCESS 0 +FAILURE 33 +--- success/failure for external command +FAILURE 1 +@todo_wine@ERRORLEVEL 3 +FAILURE 1 +ERRORLEVEL 9009 +foo@space@ +SUCCESS 0 +foo@space@ +FAILURE 1024 +FAILURE 1 +--- success/failure for CALL command +FAILURE 1 +SUCCESS 0 +FAILURE 1024 +foo@space@ +SUCCESS 0 +foo@space@ +FAILURE 1025 +FAILURE 2 +--- success/failure for pipes +b +SUCCESS 0 +FAILURE 1 +a +SUCCESS 0 +FAILURE 3 +a +SUCCESS 0 +FAILURE 255 +FAILURE 255 +--- success/failure for START command +@todo_wine@FAILURE 1 +foo@space@ +SUCCESS 1024 +@todo_wine@SUCCESS 666 +--- success/failure for TYPE command +FAILURE 1 +SUCCESS 0 +FAILURE 1 +@todo_wine@a@space@ +@todo_wine@b@space@ +@todo_wine@FAILURE 1 +@todo_wine@--- +FAILURE 1 +--- success/failure for COPY command +FAILURE 1 +SUCCESS 0 +FAILURE 1 +FAILURE 1 +FAILURE 1 +--- success/failure for MOVE command +FAILURE 1 +SUCCESS 0 +FAILURE 1 +FAILURE 1 +FAILURE 1 +--- success/failure for RENAME command +FAILURE 1 +FAILURE 1 +FAILURE 1 +FAILURE 1 +SUCCESS 0 +--- success/failure for ERASE command +FAILURE 1 +SUCCESS 0 +FAILURE 1 +FAILURE 1 +--- success/failure for change drive command +SUCCESS 666 +FAILURE 1 +SUCCESS 0 +FAILURE 1 +--- success/failure for MKDIR,MD command +FAILURE 1 +SUCCESS 0 +FAILURE 1 +FAILURE 1 +SUCCESS 0 +--- success/failure for CD command +SUCCESS 0 +FAILURE 1 +SUCCESS 0 +SUCCESS 0 +--- success/failure for PUSHD/POPD commands +SUCCESS 0 +SUCCESS 0 +FAILURE 1 +SUCCESS 666 +FAILURE 1 +ERRORLEVEL 666 +--- success/failure for DIR command +FAILURE 1 +FAILURE 1 +SUCCESS 0 +SUCCESS 0 +FAILURE 1 +SUCCESS 0 +--- success/failure for RMDIR/RD command +FAILURE 1 +FAILURE 145 +FAILURE 267 +SUCCESS 666 +FAILURE 2 +FAILURE 3 +ERRORLEVEL 666 +ERRORLEVEL 666 +ERRORLEVEL 666 +ERRORLEVEL 666 +ERRORLEVEL 666 +ERRORLEVEL 666 +--- success/failure for MKLINK command +FAILURE 1 +FAILURE 1 +FAILURE 1 +FAILURE 1 +FAILURE 1 +SUCCESS 0 +FAILURE 1 +--- success/failure for SETLOCAL/ENDLOCAL commands +FAILURE 1 +SUCCESS 0 +SUCCESS 666 +@todo_wine@SUCCESS@space@ +SUCCESS 0 +--- success/failure for DATE command +SUCCESS 0 +FAILURE 1 +--- success/failure for TIME command +SUCCESS 0 +FAILURE 1 +--- success/failure for BREAK command +@todo_wine@SUCCESS 666 +@todo_wine@SUCCESS 666 +--- success/failure for VER command +SUCCESS 0 +SUCCESS 0 +FAILURE 1 +--- success/failure for VERIFY command +SUCCESS 0 +SUCCESS 0 +FAILURE 1 +--- success/failure for VOL command +SUCCESS 0 +SUCCESS 0 +FAILURE 1 +FAILURE 1 +--- success/failure for LABEL command +FAILURE 1 +--- success/failure for PATH command +@todo_wine@SUCCESS 666 +@todo_wine@SUCCESS 666 +PATH=@:\I\dont\Exist@space@ +@todo_wine@SUCCESS 666 +PATH=(null) +@todo_wine@SUCCESS 666 +--- success/failure for SET command +@todo_wine@SUCCESS 666 +@todo_wine@SUCCESS 666 +FAILURE 1 +@todo_wine@SUCCESS 666 +@todo_wine@SUCCESS 666 +FAILURE 1 +FAILURE 1 +--- success/failure for ASSOC command +@todo_wine@SUCCESS 666 +FAILURE 1 +FAILURE 1 +--- success/failure for FTYPE command +@todo_wine@SUCCESS 666 +FAILURE 2 +FAILURE 2 +--- success/failure for SHIFT command +FAILURE 1 +SUCCESS 666 +SUCCESS 666 +--- success/failure for HELP command +FAILURE 1 +FAILURE 1 +SUCCESS 0 +--- success/failure for PROMPT command +@todo_wine@SUCCESS 666 +--- success/failure for CLS command +@todo_wine@@formfeed@SUCCESS 666 +@todo_wine@@formfeed@SUCCESS 666 +FAILURE 1 +--- success/failure for COLOR command +FAILURE 1 +--- success/failure for TITLE command +SUCCESS 666 +SUCCESS 666 +--- success/failure for CHOICE command +FAILURE 1 +FAILURE 1 +FAILURE 2 +FAILURE 1 +--- success/failure for MORE command +SUCCESS 0 +SUCCESS 0 +foo@space@ + +SUCCESS 0 +--- success/failure for PAUSE command +FAILURE 1 +--- diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 29f4ad9e9c4..01c8a73ff31 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -467,6 +467,8 @@ if 1==0 (echo q1) else echo q2&echo q3 echo ------------- Testing internal commands return codes setlocal EnableDelayedExpansion
+rem All the success/failure tests are meant to be duplicated in test_builtins.bat +rem So be sure to update both files at once echo --- success/failure for basics call :setError 0 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! call :setError 33 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!