[NTOS:KE/x64] Handle NMI vs swapgs race condition
[reactos.git] / modules / rostests / win32 / cmd / test_builtins.cmd
1 @echo off
2
3 ::
4 :: Some basic tests
5 ::
6
7 echo ------------ Testing FOR loop ------------
8 echo --- Multiple lines
9 for %%i in (A
10 B
11 C) do echo %%i
12
13 echo --- Lines and spaces
14 for %%i in (D
15 E
16 F) do echo %%i
17
18 echo --- Multiple lines and commas
19 for %%i in (G,
20 H,
21 I
22 ) do echo %%i
23
24 echo --- Multiple lines and %%I
25 :: The FOR-variable is case-sensitive
26 for %%i in (J
27 K
28 L) do echo %%I
29
30 echo --- Multiple lines and %%j
31 for %%i in (M,
32 N,
33 O
34 ) do echo %%j
35
36 echo --- FOR /F token parsing
37 :: This test requires extensions being enabled
38 setlocal enableextensions
39
40 set TEST_STRING="_ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ ? @ [ \ ] _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ ? @ [ \ ]"
41 set "ECHO_STRING=?=%%? @=%%@ A=%%A B=%%B C=%%C D=%%D E=%%E F=%%F G=%%G H=%%H I=%%I J=%%J K=%%K L=%%L M=%%M N=%%N O=%%O P=%%P Q=%%Q R=%%R S=%%S T=%%T U=%%U V=%%V W=%%W X=%%X Y=%%Y Z=%%Z [=%%[ \=%%\ ]=%%] ^^=%%^^ _=%%_ `=%%` a=%%a b=%%b c=%%c d=%%d e=%%e f=%%f g=%%g h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o p=%%p q=%%q r=%%r s=%%s t=%%t u=%%u v=%%v w=%%w x=%%x y=%%y z=%%z {=%%{ ^|=%%^| }=%%} ^~=%%^~"
42
43 echo.
44
45 :: Bug 1: Ranges that are not specified in increasing order are ignored.
46 :: Token numbers strictly greater than 31 are just ignored, and if they
47 :: appear in a range, the whole range is ignored.
48
49 for /f "tokens=30-32" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
50 echo.
51 for /f "tokens=5-1" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
52 echo.
53
54 :: Bug 2: Ranges that partially overlap: too many variables are being allocated,
55 :: while only a subset is actually used. This leads to the extra variables returning
56 :: empty strings.
57
58 for /f "tokens=1-31,31,31" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
59 echo.
60 for /f "tokens=1-31,1-31" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
61 echo.
62 for /f "tokens=1-5,3,5,6" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
63 echo.
64 for /f "tokens=1-31,* tokens=1-31 tokens=1-20,*" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
65 echo.
66
67 :: For comparison, this works:
68 for /f "tokens=1-5,6" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
69 echo.
70 for /f "tokens=1-5,6-10" %%? in (%TEST_STRING%) do echo %ECHO_STRING%
71 echo.
72
73 endlocal
74
75
76 echo ---------- Testing AND operator ----------
77 :: Test for TRUE condition - Should be displayed
78 ver | find "Ver" > NUL && echo TRUE AND condition
79
80 :: Test for FALSE condition - Should not display
81 ver | find "1234" > NUL && echo FALSE AND condition
82
83 echo ---------- Testing OR operator -----------
84 :: Test for TRUE condition - Should not display
85 ver | find "Ver" > NUL || echo TRUE OR condition
86
87 :: Test for FALSE condition - Should be displayed
88 ver | find "1234" > NUL || echo FALSE OR condition
89
90
91
92 ::
93 :: Testing CMD exit codes and errorlevels.
94 ::
95 :: Observations:
96 :: - OR operator || converts the LHS error code to ERRORLEVEL only on failure;
97 :: - Pipe operator | converts the last error code to ERRORLEVEL.
98 ::
99 :: See https://stackoverflow.com/a/34987886/13530036
100 :: and https://stackoverflow.com/a/34937706/13530036
101 :: for more details.
102 ::
103 setlocal enableextensions
104
105 echo ---------- Testing CMD exit codes and errorlevels ----------
106
107 :: Tests for CMD returned exit code.
108
109 echo --- CMD /C Direct EXIT call
110
111 call :setError 0
112 cmd /c "exit 42"
113 call :checkErrorLevel 42
114
115 call :setError 111
116 cmd /c "exit 42"
117 call :checkErrorLevel 42
118
119 echo --- CMD /C Direct EXIT /B call
120
121 call :setError 0
122 cmd /c "exit /b 42"
123 call :checkErrorLevel 42
124
125 call :setError 111
126 cmd /c "exit /b 42"
127 call :checkErrorLevel 42
128
129 :: Non-existing ccommand, or command that only changes
130 :: the returned code (but NOT the ERRORLEVEL) and EXIT.
131
132 echo --- CMD /C Non-existing command
133
134 :: EXIT alone does not change the ERRORLEVEL
135 call :setError 0
136 cmd /c "nonexisting & exit"
137 call :checkErrorLevel 9009
138
139 call :setError 111
140 cmd /c "nonexisting & exit"
141 call :checkErrorLevel 9009
142
143 call :setError 0
144 cmd /c "nonexisting & exit /b"
145 call :checkErrorLevel 9009
146
147 call :setError 111
148 cmd /c "nonexisting & exit /b"
149 call :checkErrorLevel 9009
150
151 echo --- CMD /C RMDIR (no ERRORLEVEL set)
152
153 call :setError 0
154 cmd /c "rmdir nonexisting & exit"
155 call :checkErrorLevel 0
156
157 call :setError 111
158 cmd /c "rmdir nonexisting & exit"
159 call :checkErrorLevel 0
160
161 call :setError 0
162 cmd /c "rmdir nonexisting & exit /b"
163 call :checkErrorLevel 0
164
165 call :setError 111
166 cmd /c "rmdir nonexisting & exit /b"
167 call :checkErrorLevel 0
168
169 :: Failing command (sets ERRORLEVEL to 1) and EXIT
170 echo --- CMD /C DIR (sets ERRORLEVEL) - With failure
171
172 :: EXIT alone does not change the ERRORLEVEL
173 call :setError 0
174 cmd /c "dir nonexisting>NUL & exit"
175 call :checkErrorLevel 1
176
177 call :setError 111
178 cmd /c "dir nonexisting>NUL & exit"
179 call :checkErrorLevel 1
180
181 call :setError 0
182 cmd /c "dir nonexisting>NUL & exit /b"
183 call :checkErrorLevel 1
184
185 call :setError 111
186 cmd /c "dir nonexisting>NUL & exit /b"
187 call :checkErrorLevel 1
188
189 :: Here EXIT changes the ERRORLEVEL
190 call :setError 0
191 cmd /c "dir nonexisting>NUL & exit 42"
192 call :checkErrorLevel 42
193
194 call :setError 111
195 cmd /c "dir nonexisting>NUL & exit 42"
196 call :checkErrorLevel 42
197
198 call :setError 0
199 cmd /c "dir nonexisting>NUL & exit /b 42"
200 call :checkErrorLevel 42
201
202 call :setError 111
203 cmd /c "dir nonexisting>NUL & exit /b 42"
204 call :checkErrorLevel 42
205
206 :: Succeeding command (sets ERRORLEVEL to 0) and EXIT
207 echo --- CMD /C DIR (sets ERRORLEVEL) - With success
208
209 call :setError 0
210 cmd /c "dir>NUL & exit"
211 call :checkErrorLevel 0
212
213 call :setError 111
214 cmd /c "dir>NUL & exit"
215 call :checkErrorLevel 0
216
217 call :setError 0
218 cmd /c "dir>NUL & exit 42"
219 call :checkErrorLevel 42
220
221 call :setError 111
222 cmd /c "dir>NUL & exit 42"
223 call :checkErrorLevel 42
224
225 call :setError 0
226 cmd /c "dir>NUL & exit /b 42"
227 call :checkErrorLevel 42
228
229 call :setError 111
230 cmd /c "dir>NUL & exit /b 42"
231 call :checkErrorLevel 42
232
233
234 :: Same sorts of tests, but now from within an external batch file:
235 :: Tests for CALL command returned exit code.
236
237 :: Use an auxiliary CMD file
238 mkdir foobar && cd foobar
239
240 :: Non-existing ccommand, or command that only changes
241 :: the returned code (but NOT the ERRORLEVEL) and EXIT.
242
243 echo --- CALL Batch Non-existing command
244
245 :: EXIT alone does not change the ERRORLEVEL
246 echo nonexisting ^& exit /b> tmp.cmd
247 call :setError 0
248 call tmp.cmd
249 call :checkErrorLevel 9009
250
251 echo nonexisting ^& exit /b> tmp.cmd
252 call :setError 111
253 call tmp.cmd
254 call :checkErrorLevel 9009
255
256 :: These tests show that || converts the returned error code
257 :: from RMDIR on failure, and converts it to an ERRORLEVEL
258 :: (first two tests: no ||, thus no ERRORLEVEL set;
259 :: last two tests: ||used and ERRORLEVEL is set).
260 ::
261
262 echo --- CALL Batch RMDIR (no ERRORLEVEL set)
263
264 :: This test shows that if a batch returns error code 0 from CALL,
265 :: then CALL will keep the existing ERRORLEVEL (here, 111)...
266 echo rmdir nonexisting> tmp.cmd
267 echo exit /b>> tmp.cmd
268 call :setError 0
269 call tmp.cmd
270 call :checkErrorLevel 0
271
272 echo rmdir nonexisting> tmp.cmd
273 echo exit /b>> tmp.cmd
274 call :setError 111
275 call tmp.cmd
276 call :checkErrorLevel 111
277
278 echo --- CALL Batch RMDIR with ^|^| (sets ERRORLEVEL)
279
280 :: ... but if a non-zero error code is returned from CALL,
281 :: then CALL uses it as the new ERRORLEVEL.
282 echo rmdir nonexisting ^|^| rem> tmp.cmd
283 echo exit /b>> tmp.cmd
284 call :setError 0
285 call tmp.cmd
286 call :checkErrorLevel 2
287 :: This gives the same effect, since the last command's error code
288 :: is returned and transformed by CALL into an ERRORLEVEL:
289 echo rmdir nonexisting> tmp.cmd
290 call :setError 0
291 call tmp.cmd
292 call :checkErrorLevel 2
293
294 echo rmdir nonexisting ^|^| rem> tmp.cmd
295 echo exit /b>> tmp.cmd
296 call :setError 111
297 call tmp.cmd
298 call :checkErrorLevel 2
299 :: This gives the same effect, since the last command's error code
300 :: is returned and transformed by CALL into an ERRORLEVEL:
301 echo rmdir nonexisting> tmp.cmd
302 call :setError 111
303 call tmp.cmd
304 call :checkErrorLevel 2
305
306
307 :: Failing command (sets ERRORLEVEL to 1) and EXIT
308 echo --- CALL Batch DIR (sets ERRORLEVEL) - With failure
309
310 echo dir nonexisting^>NUL> tmp.cmd
311 call :setError 0
312 call tmp.cmd
313 call :checkErrorLevel 1
314
315 echo dir nonexisting^>NUL> tmp.cmd
316 call :setError 111
317 call tmp.cmd
318 call :checkErrorLevel 1
319
320 echo dir nonexisting^>NUL ^& goto :eof> tmp.cmd
321 call :setError 0
322 call tmp.cmd
323 call :checkErrorLevel 1
324
325 echo dir nonexisting^>NUL ^& goto :eof> tmp.cmd
326 call :setError 111
327 call tmp.cmd
328 call :checkErrorLevel 1
329
330 echo dir nonexisting^>NUL ^& exit /b> tmp.cmd
331 call :setError 0
332 call tmp.cmd
333 call :checkErrorLevel 1
334
335 echo dir nonexisting^>NUL ^& exit /b> tmp.cmd
336 call :setError 111
337 call tmp.cmd
338 call :checkErrorLevel 1
339
340 echo dir nonexisting^>NUL ^& exit /b 42 > tmp.cmd
341 call :setError 0
342 call tmp.cmd
343 call :checkErrorLevel 42
344
345 echo dir nonexisting^>NUL ^& exit /b 42 > tmp.cmd
346 call :setError 111
347 call tmp.cmd
348 call :checkErrorLevel 42
349
350 :: Succeeding command (sets ERRORLEVEL to 0) and EXIT
351 echo --- CALL Batch DIR (sets ERRORLEVEL) - With success
352
353 echo dir^>NUL> tmp.cmd
354 call :setError 0
355 call tmp.cmd
356 call :checkErrorLevel 0
357
358 echo dir^>NUL> tmp.cmd
359 call :setError 111
360 call tmp.cmd
361 call :checkErrorLevel 0
362
363 echo dir^>NUL ^& goto :eof> tmp.cmd
364 call :setError 0
365 call tmp.cmd
366 call :checkErrorLevel 0
367
368 echo dir^>NUL ^& goto :eof> tmp.cmd
369 call :setError 111
370 call tmp.cmd
371 call :checkErrorLevel 0
372
373 echo dir^>NUL ^& exit /b> tmp.cmd
374 call :setError 0
375 call tmp.cmd
376 call :checkErrorLevel 0
377
378 echo dir^>NUL ^& exit /b> tmp.cmd
379 call :setError 111
380 call tmp.cmd
381 call :checkErrorLevel 0
382
383 echo dir^>NUL ^& exit /b 42 > tmp.cmd
384 call :setError 0
385 call tmp.cmd
386 call :checkErrorLevel 42
387
388 echo dir^>NUL ^& exit /b 42 > tmp.cmd
389 call :setError 111
390 call tmp.cmd
391 call :checkErrorLevel 42
392
393
394 :: Cleanup
395 del tmp.cmd
396 cd .. & rmdir /s/q foobar
397
398
399 ::
400 :: ERRORLEVEL tests for special commands.
401 ::
402 :: For some commands, the errorlevel is set differently,
403 :: whether or not they are run within a .BAT, a .CMD, or
404 :: directly from the command-line.
405 ::
406 :: These commands are:
407 :: APPEND/DPATH, ASSOC, FTYPE, PATH, PROMPT, SET.
408 ::
409 :: See https://ss64.com/nt/errorlevel.html for more details.
410 ::
411
412 echo ---------- Testing ERRORLEVEL in .BAT and .CMD ----------
413
414 :: Use an auxiliary CMD file
415 mkdir foobar && cd foobar
416
417 echo --- In .BAT file
418 call :outputErrLvlTestToBatch tmp.bat
419 cmd /c tmp.bat
420 del tmp.bat
421
422 echo --- In .CMD file
423 call :outputErrLvlTestToBatch tmp.cmd
424 cmd /c tmp.cmd
425 del tmp.cmd
426
427 :: Cleanup
428 cd .. & rmdir /s/q foobar
429
430 :: Go to the next tests below.
431 goto :continue
432
433 :: ERRORLEVEL test helper function
434 :outputErrLvlTestToBatch (filename)
435
436 echo @echo off> %1
437 echo setlocal enableextensions>> %1
438
439 :: Reset the errorlevel
440 echo call :zeroErrLvl>> %1
441
442 :: dpath
443 :: echo %errorlevel%
444 :: dpath xxx
445 :: echo %errorlevel%
446 :: dpath
447 :: echo %errorlevel%
448
449 echo assoc^>NUL>> %1
450 echo echo %%errorlevel%%>> %1
451 echo assoc .nonexisting^>NUL>> %1
452 echo echo %%errorlevel%%>> %1
453 echo assoc .nonexisting^=^>NUL>> %1
454 echo echo %%errorlevel%%>> %1
455
456 :: ftype
457 :: echo %errorlevel%
458 :: ftype xxx
459 :: echo %errorlevel%
460 :: ftype
461 :: echo %errorlevel%
462
463 echo path^>NUL>> %1
464 echo echo %%errorlevel%%>> %1
465 echo path^;^>NUL>> %1
466 echo echo %%errorlevel%%>> %1
467
468 echo prompt^>NUL>> %1
469 echo echo %%errorlevel%%>> %1
470 echo prompt ^$p^$g^>NUL>> %1
471 echo echo %%errorlevel%%>> %1
472 echo prompt foobar^>NUL>> %1
473 echo echo %%errorlevel%%>> %1
474
475 echo set^>NUL>> %1
476 echo echo %%errorlevel%%>> %1
477 echo set nonexisting^>NUL>> %1
478 echo echo %%errorlevel%%>> %1
479 echo set nonexisting^=^>NUL>> %1
480 echo echo %%errorlevel%%>> %1
481 echo set nonexisting^=trololol^>NUL>> %1
482 echo echo %%errorlevel%%>> %1
483 echo set nonexisting^=^>NUL>> %1
484 echo echo %%errorlevel%%>> %1
485
486 echo goto :eof>> %1
487
488 :: Zero ERRORLEVEL
489 echo :zeroErrLvl>> %1
490 echo exit /B 0 >> %1
491
492 goto :eof
493
494
495 ::
496 :: Next suite of tests.
497 ::
498 :continue
499
500
501 :: Testing different ERRORLEVELs from the SET command.
502 :: See https://ss64.com/nt/set.html for more details.
503
504 echo ---------- Testing SET /A ERRORLEVELs ----------
505
506 echo --- Success
507 call :setError 0
508 set /a "total=1+1"
509 call :checkErrorLevel 0
510 echo %errorlevel%
511 echo %total%
512
513 echo --- Unbalanced parentheses
514 call :setError 0
515 set /a "total=(2+1"
516 call :checkErrorLevel 1073750988
517 echo %errorlevel%
518 echo %total%
519
520 echo --- Missing operand
521 call :setError 0
522 set /a "total=5*"
523 call :checkErrorLevel 1073750989
524 echo %errorlevel%
525 echo %total%
526
527 echo --- Syntax error
528 call :setError 0
529 set /a "total=7$3"
530 call :checkErrorLevel 1073750990
531 echo %errorlevel%
532 echo %total%
533
534 echo --- Invalid number
535 call :setError 0
536 set /a "total=0xdeadbeeg"
537 call :checkErrorLevel 1073750991
538 echo %errorlevel%
539 echo %total%
540
541 echo --- Number larger than 32-bits
542 call :setError 0
543 set /a "total=999999999999999999999999"
544 call :checkErrorLevel 1073750992
545 echo %errorlevel%
546 echo %total%
547
548 echo --- Division by zero
549 call :setError 0
550 set /a "total=1/0"
551 call :checkErrorLevel 1073750993
552 echo %errorlevel%
553 echo %total%
554
555
556
557 ::
558 :: Finished!
559 ::
560 echo --------- Finished --------------
561 goto :EOF
562
563 :checkErrorLevel
564 if %errorlevel% neq %1 (echo Unexpected errorlevel %errorlevel%, expected %1) else echo OK
565 goto :eof
566
567 :: Subroutine to set errorlevel and return
568 :: in windows nt 4.0, this always sets errorlevel 1, since /b isn't supported
569 :setError
570 exit /B %1
571 :: This line runs under cmd in windows NT 4, but not in more modern versions.