https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4c9d322c688054b74760c…
commit 4c9d322c688054b74760cde38fc431589a6408f4
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Wed Jul 29 22:31:23 2020 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Wed Aug 19 20:36:02 2020 +0200
[CMD_ROSTEST] Add tests for EXIT and CMD returned exit code values.
CORE-10495 CORE-13672
See also commits 8cf11060 (r40474), 26ff2c8e and 7bd33ac4.
More information can be also found at:
https://stackoverflow.com/a/34987886/13530036
and
https://stackoverflow.com/a/34937706/13530036
---
modules/rostests/win32/cmd/test_builtins.cmd | 328 ++++++++++++++++++++++-
modules/rostests/win32/cmd/test_builtins.cmd.exp | 64 ++++-
2 files changed, 390 insertions(+), 2 deletions(-)
diff --git a/modules/rostests/win32/cmd/test_builtins.cmd
b/modules/rostests/win32/cmd/test_builtins.cmd
index dd4e1af9eab..9d03f175dc6 100644
--- a/modules/rostests/win32/cmd/test_builtins.cmd
+++ b/modules/rostests/win32/cmd/test_builtins.cmd
@@ -1,5 +1,9 @@
@echo off
+::
+:: Some basic tests
+::
+
echo ------------ Testing FOR loop ------------
echo --- Multiple lines
for %%i in (A
@@ -45,4 +49,326 @@ ver | find "Ver" > NUL || echo TRUE OR condition
ver | find "1234" > NUL || echo FALSE OR condition
-echo ------------- End of Testing -------------
+
+::
+:: Testing CMD exit codes and errorlevels.
+::
+:: Observations:
+:: - OR operator || converts the LHS error code to ERRORLEVEL only on failure;
+:: - Pipe operator | converts the last error code to ERRORLEVEL.
+::
+:: See
https://stackoverflow.com/a/34987886/13530036
+:: and
https://stackoverflow.com/a/34937706/13530036
+:: for more details.
+::
+setlocal enableextensions
+
+echo ---------- Testing CMD exit codes and errorlevels ----------
+
+:: Tests for CMD returned exit code.
+
+echo --- CMD /C Direct EXIT call
+
+call :setError 0
+cmd /c "exit 42"
+call :checkErrorLevel 42
+
+call :setError 111
+cmd /c "exit 42"
+call :checkErrorLevel 42
+
+echo --- CMD /C Direct EXIT /B call
+
+call :setError 0
+cmd /c "exit /b 42"
+call :checkErrorLevel 42
+
+call :setError 111
+cmd /c "exit /b 42"
+call :checkErrorLevel 42
+
+:: Non-existing ccommand, or command that only changes
+:: the returned code (but NOT the ERRORLEVEL) and EXIT.
+
+echo --- CMD /C Non-existing command
+
+:: EXIT alone does not change the ERRORLEVEL
+call :setError 0
+cmd /c "nonexisting & exit"
+call :checkErrorLevel 9009
+
+call :setError 111
+cmd /c "nonexisting & exit"
+call :checkErrorLevel 9009
+
+call :setError 0
+cmd /c "nonexisting & exit /b"
+call :checkErrorLevel 9009
+
+call :setError 111
+cmd /c "nonexisting & exit /b"
+call :checkErrorLevel 9009
+
+echo --- CMD /C RMDIR (no ERRORLEVEL set)
+
+call :setError 0
+cmd /c "rmdir nonexisting & exit"
+call :checkErrorLevel 0
+
+call :setError 111
+cmd /c "rmdir nonexisting & exit"
+call :checkErrorLevel 0
+
+call :setError 0
+cmd /c "rmdir nonexisting & exit /b"
+call :checkErrorLevel 0
+
+call :setError 111
+cmd /c "rmdir nonexisting & exit /b"
+call :checkErrorLevel 0
+
+:: Failing command (sets ERRORLEVEL to 1) and EXIT
+echo --- CMD /C DIR (sets ERRORLEVEL) - With failure
+
+:: EXIT alone does not change the ERRORLEVEL
+call :setError 0
+cmd /c "dir nonexisting>NUL & exit"
+call :checkErrorLevel 1
+
+call :setError 111
+cmd /c "dir nonexisting>NUL & exit"
+call :checkErrorLevel 1
+
+call :setError 0
+cmd /c "dir nonexisting>NUL & exit /b"
+call :checkErrorLevel 1
+
+call :setError 111
+cmd /c "dir nonexisting>NUL & exit /b"
+call :checkErrorLevel 1
+
+:: Here EXIT changes the ERRORLEVEL
+call :setError 0
+cmd /c "dir nonexisting>NUL & exit 42"
+call :checkErrorLevel 42
+
+call :setError 111
+cmd /c "dir nonexisting>NUL & exit 42"
+call :checkErrorLevel 42
+
+call :setError 0
+cmd /c "dir nonexisting>NUL & exit /b 42"
+call :checkErrorLevel 42
+
+call :setError 111
+cmd /c "dir nonexisting>NUL & exit /b 42"
+call :checkErrorLevel 42
+
+:: Succeeding command (sets ERRORLEVEL to 0) and EXIT
+echo --- CMD /C DIR (sets ERRORLEVEL) - With success
+
+call :setError 0
+cmd /c "dir>NUL & exit"
+call :checkErrorLevel 0
+
+call :setError 111
+cmd /c "dir>NUL & exit"
+call :checkErrorLevel 0
+
+call :setError 0
+cmd /c "dir>NUL & exit 42"
+call :checkErrorLevel 42
+
+call :setError 111
+cmd /c "dir>NUL & exit 42"
+call :checkErrorLevel 42
+
+call :setError 0
+cmd /c "dir>NUL & exit /b 42"
+call :checkErrorLevel 42
+
+call :setError 111
+cmd /c "dir>NUL & exit /b 42"
+call :checkErrorLevel 42
+
+
+:: Same sorts of tests, but now from within an external batch file:
+:: Tests for CALL command returned exit code.
+
+:: Use an auxiliary CMD file
+mkdir foobar && cd foobar
+
+:: Non-existing ccommand, or command that only changes
+:: the returned code (but NOT the ERRORLEVEL) and EXIT.
+
+echo --- CALL Batch Non-existing command
+
+:: EXIT alone does not change the ERRORLEVEL
+echo nonexisting ^& exit /b> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 9009
+
+echo nonexisting ^& exit /b> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 9009
+
+:: These tests show that || converts the returned error code
+:: from RMDIR on failure, and converts it to an ERRORLEVEL
+:: (first two tests: no ||, thus no ERRORLEVEL set;
+:: last two tests: ||used and ERRORLEVEL is set).
+::
+
+echo --- CALL Batch RMDIR (no ERRORLEVEL set)
+
+:: This test shows that if a batch returns error code 0 from CALL,
+:: then CALL will keep the existing ERRORLEVEL (here, 111)...
+echo rmdir nonexisting> tmp.cmd
+echo exit /b>> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 0
+
+echo rmdir nonexisting> tmp.cmd
+echo exit /b>> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 111
+
+echo --- CALL Batch RMDIR with ^|^| (sets ERRORLEVEL)
+
+:: ... but if a non-zero error code is returned from CALL,
+:: then CALL uses it as the new ERRORLEVEL.
+echo rmdir nonexisting ^|^| rem> tmp.cmd
+echo exit /b>> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 2
+:: This gives the same effect, since the last command's error code
+:: is returned and transformed by CALL into an ERRORLEVEL:
+echo rmdir nonexisting> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 2
+
+echo rmdir nonexisting ^|^| rem> tmp.cmd
+echo exit /b>> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 2
+:: This gives the same effect, since the last command's error code
+:: is returned and transformed by CALL into an ERRORLEVEL:
+echo rmdir nonexisting> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 2
+
+
+:: Failing command (sets ERRORLEVEL to 1) and EXIT
+echo --- CALL Batch DIR (sets ERRORLEVEL) - With failure
+
+echo dir nonexisting^>NUL> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 1
+
+echo dir nonexisting^>NUL> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 1
+
+echo dir nonexisting^>NUL ^& goto :eof> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 1
+
+echo dir nonexisting^>NUL ^& goto :eof> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 1
+
+echo dir nonexisting^>NUL ^& exit /b> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 1
+
+echo dir nonexisting^>NUL ^& exit /b> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 1
+
+echo dir nonexisting^>NUL ^& exit /b 42 > tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 42
+
+echo dir nonexisting^>NUL ^& exit /b 42 > tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 42
+
+:: Succeeding command (sets ERRORLEVEL to 0) and EXIT
+echo --- CALL Batch DIR (sets ERRORLEVEL) - With success
+
+echo dir^>NUL> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 0
+
+echo dir^>NUL> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 0
+
+echo dir^>NUL ^& goto :eof> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 0
+
+echo dir^>NUL ^& goto :eof> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 0
+
+echo dir^>NUL ^& exit /b> tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 0
+
+echo dir^>NUL ^& exit /b> tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 0
+
+echo dir^>NUL ^& exit /b 42 > tmp.cmd
+call :setError 0
+call tmp.cmd
+call :checkErrorLevel 42
+
+echo dir^>NUL ^& exit /b 42 > tmp.cmd
+call :setError 111
+call tmp.cmd
+call :checkErrorLevel 42
+
+
+:: Cleanup
+del tmp.cmd
+cd .. & rmdir /s/q foobar
+
+
+::
+:: Finished!
+::
+echo --------- Finished --------------
+goto :EOF
+
+:checkErrorLevel
+if %errorlevel% neq %1 (echo Unexpected errorlevel %errorlevel%, expected %1) else echo
OK
+goto :eof
+
+:: Subroutine to set errorlevel and return
+:: in windows nt 4.0, this always sets errorlevel 1, since /b isn't supported
+:setError
+exit /B %1
+:: This line runs under cmd in windows NT 4, but not in more modern versions.
diff --git a/modules/rostests/win32/cmd/test_builtins.cmd.exp
b/modules/rostests/win32/cmd/test_builtins.cmd.exp
index 43f4a6b5b30..e2d609e8e03 100644
--- a/modules/rostests/win32/cmd/test_builtins.cmd.exp
+++ b/modules/rostests/win32/cmd/test_builtins.cmd.exp
@@ -23,4 +23,66 @@ I
TRUE AND condition
---------- Testing OR operator -----------
FALSE OR condition
-------------- End of Testing -------------
+---------- Testing CMD exit codes and errorlevels ----------
+--- CMD /C Direct EXIT call
+OK
+OK
+--- CMD /C Direct EXIT /B call
+OK
+OK
+--- CMD /C Non-existing command
+OK
+OK
+OK
+OK
+--- CMD /C RMDIR (no ERRORLEVEL set)
+OK
+OK
+OK
+OK
+--- CMD /C DIR (sets ERRORLEVEL) - With failure
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+--- CMD /C DIR (sets ERRORLEVEL) - With success
+OK
+OK
+OK
+OK
+OK
+OK
+--- CALL Batch Non-existing command
+OK
+OK
+--- CALL Batch RMDIR (no ERRORLEVEL set)
+OK
+OK
+--- CALL Batch RMDIR with || (sets ERRORLEVEL)
+OK
+OK
+OK
+OK
+--- CALL Batch DIR (sets ERRORLEVEL) - With failure
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+--- CALL Batch DIR (sets ERRORLEVEL) - With success
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+--------- Finished --------------