90be4aeba1aa9ca2422ca98fc8cb2c12c48cea12
[platform/upstream/dotnet/runtime.git] / runtest_clr.sh
1 #!/usr/bin/env bash
2
3 function print_usage {
4     echo ''
5     echo 'CoreCLR test runner script.'
6     echo 'Script uses next files if they exist:'
7     echo '    unsupportedTests.txt'
8     echo '    unsupportedCrossgenLibs.txt'
9     echo '    unsupportedCrossgenTests.txt'
10     echo ''
11     echo 'Typical Tizen command:'
12     echo '    coreclr/tests/runtest.sh'
13     echo '    --arch=armel'
14     echo '    --testRootDir="/opt/usr/coreclr-tc"'
15     echo '    --coreOverlayDir="/opt/usr/coreclr-tc/coreroot"'
16     echo '    --netcoreDir="/usr/share/dotnet.tizen/netcoreapp"'
17     echo '    --copy-netcore-to-coreroot'
18     echo '    --xunitOutputPath="/opt/usr/coreclr-tc/results/coreclrtests.xml"'
19     echo '    --testsPassOutputPath="/opt/usr/coreclr-tc/results/coreclrtests.pass.txt"'
20     echo '    --testsSkipOutputPath="/opt/usr/coreclr-tc/results/coreclrtests.skip.txt"'
21     echo '    --testsFailOutputPath="/opt/usr/coreclr-tc/results/coreclrtests.fail.txt"'
22     echo '    --libsCrossgenPassOutputPath="/opt/usr/coreclr-tc/results/crossgenlibs.pass.txt"'
23     echo '    --libsCrossgenSkipOutputPath="/opt/usr/coreclr-tc/results/crossgenlibs.skip.txt"'
24     echo '    --libsCrossgenFailOutputPath="/opt/usr/coreclr-tc/results/crossgenlibs.fail.txt"'
25     echo '    --crossgen2options="--jitpath /opt/usr/coreclr-tc/coreroot/libclrjit.so --targetarch armel --parallelism 4 -O"'
26     echo ''
27     echo 'Required arguments:'
28     echo '  --arch=<arch>                    : Target arch (for TW targets "armel" should be passed instead of "arm64", for TM use "armel"; allowed values: "arm", "armel", "arm64", "x64", "x86", "riscv64").'
29     echo '  --testRootDir=<path>             : Root directory of the test build (default: /opt/usr/coreclr-tc).'
30     echo '  --coreOverlayDir=<path>          : Directory containing CLR and FX (default: /opt/usr/coreclr-tc/coreroot).'
31     echo ''
32     echo 'Optional arguments:'
33     echo '  --netcoreDir=<path>              : Netcore (CLR and FX) system dir (default: /usr/share/dotnet.tizen/netcoreapp).'
34     echo '  --copy-netcore-to-coreroot       : Copy netcore (CLR and FX) from netcore system dir to coreroot.'
35     echo '  --testDir=<path>                 : Run tests only in the specified directory. The path is relative to the directory'
36     echo '                                     specified by --testRootDir. Multiple of this switch may be specified.'
37     echo '  --testDirFile=<path>             : Run tests only in the directories specified by the file at <path>. Paths are listed'
38     echo '                                     one line, relative to the directory specified by --testRootDir.'
39     echo '  --num-procs=<N>                  : Run N test processes at the same time (default is 1).'
40     echo '  -v, --verbose                    : Show output from each test.'
41     echo '  -h, --help                       : Show usage information.'
42     echo '  --xunitOutputPath=<path>         : Create xUnit XML report at the specifed path (default: <test root dir>/coreclrtests.xml)'
43     echo '  --testsPassOutputPath=<path>     : Create pass report at the specifed path (default: <test root dir>/coreclrtests.pass.txt)'
44     echo '  --testsSkipOutputPath=<path>     : Create skip report at the specifed path (default: <test root dir>/coreclrtests.skip.txt)'
45     echo '  --testsFailOutputPath=<path>     : Create fail report at the specifed path (default: <test root dir>/coreclrtests.fail.txt)'
46     echo '  --test-env                       : Script to set environment variables for tests.'
47     echo ''
48     echo 'Optional arguments with "export COMPlus_..." meaning:'
49     echo '  --jitstress=<n>                  : Runs the tests with COMPlus_JitStress=n'
50     echo '  --jitstressregs=<n>              : Runs the tests with COMPlus_JitStressRegs=n'
51     echo '  --gcstresslevel=<n>              : Runs the tests with COMPlus_GCStress=n'
52     echo ''
53     echo 'MulticoreJit:'
54     echo '  --mcj                            : Runs the tests with COMPlus_MultiCoreJitProfile=<some_tmp_path> COMPlus_MultiCoreJitMinNumCpus=1 (each test is launched 2 times: first launch to gather profile, second launch to use profile)'
55     echo '  --mcj-no-profile-gather          : Runs the tests with COMPlus_MultiCoreJitNoProfileGather=1 on the second mcj launch'
56     echo ''
57     echo 'Crossgen:'
58     echo '  --crossgen-libs                  : Precompiles system libs.'
59     echo '  --crossgen-tests                 : Precompiles tests.'
60     echo '  --crossgenoptions                : Options passed to crossgen.'
61     echo '  --allow-crossgen-fails           : Allow crossgen to ignore compilation fails.'
62     echo '  --crossgen-lib=<path>            : Precompile only specified lib. The path is absolute.'
63     echo '  --libsCrossgenPassOutputPath=<path>  : Create crossgen pass report at the specifed path (default: <test root dir>/crossgenlibs.pass.txt)'
64     echo '  --libsCrossgenSkipOutputPath=<path>  : Create crossgen skip report at the specifed path (default: <test root dir>/crossgenlibs.skip.txt)'
65     echo '  --libsCrossgenFailOutputPath=<path>  : Create crossgen fail report at the specifed path (default: <test root dir>/crossgenlibs.fail.txt)'
66     echo ''
67     echo 'Temporary:'
68     echo '  --crossgen2options               : Options passed to crossgen2 (default: --jitpath /opt/usr/coreclr-tc/coreroot/libclrjit.so --targetarch armel)'
69     echo '  --crossgen2-libs                 : Use crossgen2 for compilation of system libs.'
70     echo '  --crossgen2-tests                : Use crossgen2 for compilation of tests.'
71     echo '  --crossgen-spc-first             : Crossgen System.Private.CoreLib.dll before other dlls.'
72 }
73
74 function print_results {
75     echo ""
76     echo "======================="
77     echo "     Test Results"
78     echo "======================="
79     echo "# Overlay          : $coreOverlayDir"
80     echo "# Tests Discovered : $countTotalTests"
81     echo "# Passed           : $countPassedTests"
82     echo "# Failed           : $countFailedTests"
83     echo "# Skipped          : $countSkippedTests"
84     echo "======================="
85 }
86
87 # Initialize counters for bookkeeping.
88 countTotalTests=0
89 countPassedTests=0
90 countFailedTests=0
91 countSkippedTests=0
92
93 # Variables for xUnit-style XML output. XML format: https://xunit.github.io/docs/format-xml-v2.html
94 xunitOutputPath=
95 xunitTestOutputPath=
96
97 # Variables for text file output. These can be passed back to runtest.sh using the "--playlist" argument
98 # to rerun specific tests.
99 testsPassOutputPath=
100 testsFailOutputPath=
101 testsSkipOutputPath=
102 libsCrossgenPassOutputPath=
103 libsCrossgenFailOutputPath=
104 libsCrossgenSkipOutputPath=
105
106 function xunit_output_begin {
107     if [ -z "$xunitOutputPath" ]; then
108         xunitOutputPath=$testRootDir/coreclrtests.xml
109     fi
110     xunitTestOutputPath=${xunitOutputPath}.test
111     if [ -f "$xunitOutputPath" ]; then
112         rm -f -r "$xunitOutputPath"
113     fi
114     if [ -f "$xunitTestOutputPath" ]; then
115         rm -f -r "$xunitTestOutputPath"
116     fi
117 }
118
119 function xunit_output_add_test {
120     # <assemblies>
121     #   <assembly>
122     #     <collection>
123     #       <test .../> <!-- Write this element here -->
124
125     local scriptFilePath=$1
126     local outputFilePath=$2
127     local testResult=$3 # Pass, Fail, or Skip
128     local testScriptExitCode=$4
129     local testRunningTime=$5
130
131     local testPath=${scriptFilePath%.sh} # Remove trailing ".sh"
132     local testDir=$(dirname "$testPath")
133     local testName=$(basename "$testPath")
134
135     # Replace '/' with '.'
136     testPath=$(echo "$testPath" | tr / .)
137     testDir=$(echo "$testDir" | tr / .)
138
139     local line
140
141     line="      "
142     line="${line}<test"
143     line="${line} name=\"${testPath}\""
144     line="${line} type=\"${testDir}\""
145     line="${line} method=\"${testName}\""
146     line="${line} result=\"${testResult}\""
147     if [ -n "$testRunningTime" ] && [ "$testResult" != "Skip" ]; then
148         line="${line} time=\"${testRunningTime}\""
149     fi
150
151     if [ "$testResult" == "Pass" ]; then
152         line="${line}/>"
153         echo "$line" >>"$xunitTestOutputPath"
154         return
155     fi
156
157     line="${line}>"
158     echo "$line" >>"$xunitTestOutputPath"
159
160     line="        "
161     if [ "$testResult" == "Skip" ]; then
162         line="${line}<reason><![CDATA[$(cat "$outputFilePath")]]></reason>"
163         echo "$line" >>"$xunitTestOutputPath"
164     else
165         line="${line}<failure exception-type=\"Exit code: ${testScriptExitCode}\">"
166         echo "$line" >>"$xunitTestOutputPath"
167
168         line="          "
169         line="${line}<message>"
170         echo "$line" >>"$xunitTestOutputPath"
171         line="            "
172         line="${line}<![CDATA["
173         echo "$line" >>"$xunitTestOutputPath"
174         cat "$outputFilePath" >>"$xunitTestOutputPath"
175         line="            "
176         line="${line}]]>"
177         echo "$line" >>"$xunitTestOutputPath"
178         line="          "
179         line="${line}</message>"
180         echo "$line" >>"$xunitTestOutputPath"
181
182         line="        "
183         line="${line}</failure>"
184         echo "$line" >>"$xunitTestOutputPath"
185     fi
186
187     line="      "
188     line="${line}</test>"
189     echo "$line" >>"$xunitTestOutputPath"
190 }
191
192 function xunit_output_end {
193     local errorSource=$1
194     local errorMessage=$2
195
196     local errorCount
197     if [ -z "$errorSource" ]; then
198         ((errorCount = 0))
199     else
200         ((errorCount = 1))
201     fi
202
203     echo '<?xml version="1.0" encoding="utf-8"?>' >>"$xunitOutputPath"
204     echo '<assemblies>' >>"$xunitOutputPath"
205
206     local line
207
208     # <assembly ...>
209     line="  "
210     line="${line}<assembly"
211     line="${line} name=\"CoreClrTestAssembly\""
212     line="${line} total=\"${countTotalTests}\""
213     line="${line} passed=\"${countPassedTests}\""
214     line="${line} failed=\"${countFailedTests}\""
215     line="${line} skipped=\"${countSkippedTests}\""
216     line="${line} errors=\"${errorCount}\""
217     line="${line}>"
218     echo "$line" >>"$xunitOutputPath"
219
220     # <collection ...>
221     line="    "
222     line="${line}<collection"
223     line="${line} name=\"CoreClrTestCollection\""
224     line="${line} total=\"${countTotalTests}\""
225     line="${line} passed=\"${countPassedTests}\""
226     line="${line} failed=\"${countFailedTests}\""
227     line="${line} skipped=\"${countSkippedTests}\""
228     line="${line}>"
229     echo "$line" >>"$xunitOutputPath"
230
231     # <test .../> <test .../> ...
232     if [ -f "$xunitTestOutputPath" ]; then
233         cat "$xunitTestOutputPath" >>"$xunitOutputPath"
234         rm -f "$xunitTestOutputPath"
235     fi
236
237     # </collection>
238     line="    "
239     line="${line}</collection>"
240     echo "$line" >>"$xunitOutputPath"
241
242     if [ -n "$errorSource" ]; then
243         # <errors>
244         line="    "
245         line="${line}<errors>"
246         echo "$line" >>"$xunitOutputPath"
247
248         # <error ...>
249         line="      "
250         line="${line}<error"
251         line="${line} type=\"TestHarnessError\""
252         line="${line} name=\"${errorSource}\""
253         line="${line}>"
254         echo "$line" >>"$xunitOutputPath"
255
256         # <failure .../>
257         line="        "
258         line="${line}<failure>${errorMessage}</failure>"
259         echo "$line" >>"$xunitOutputPath"
260
261         # </error>
262         line="      "
263         line="${line}</error>"
264         echo "$line" >>"$xunitOutputPath"
265
266         # </errors>
267         line="    "
268         line="${line}</errors>"
269         echo "$line" >>"$xunitOutputPath"
270     fi
271
272     # </assembly>
273     line="  "
274     line="${line}</assembly>"
275     echo "$line" >>"$xunitOutputPath"
276
277     # </assemblies>
278     echo '</assemblies>' >>"$xunitOutputPath"
279 }
280
281 function text_file_output_begin {
282     if [ -z "$testsPassOutputPath" ]; then
283         testsPassOutputPath=$testRootDir/coreclrtests.pass.txt
284     fi
285     if [ -f "$testsPassOutputPath" ]; then
286         rm -f "$testsPassOutputPath"
287     fi
288     if [ -z "$testsFailOutputPath" ]; then
289         testsFailOutputPath=$testRootDir/coreclrtests.fail.txt
290     fi
291     if [ -f "$testsFailOutputPath" ]; then
292         rm -f "$testsFailOutputPath"
293     fi
294     if [ -z "$testsSkipOutputPath" ]; then
295         testsSkipOutputPath=$testRootDir/coreclrtests.skip.txt
296     fi
297     if [ -f "$testsSkipOutputPath" ]; then
298         rm -f "$testsSkipOutputPath"
299     fi
300
301     if [ -z "$libsCrossgenPassOutputPath" ]; then
302         libsCrossgenPassOutputPath=$testRootDir/crossgenlibs.pass.txt
303     fi
304     if [ -f "$libsCrossgenPassOutputPath" ]; then
305         rm -f "$libsCrossgenPassOutputPath"
306     fi
307     if [ -z "$libsCrossgenFailOutputPath" ]; then
308         libsCrossgenFailOutputPath=$testRootDir/crossgenlibs.fail.txt
309     fi
310     if [ -f "$libsCrossgenFailOutputPath" ]; then
311         rm -f "$libsCrossgenFailOutputPath"
312     fi
313     if [ -z "$libsCrossgenSkipOutputPath" ]; then
314         libsCrossgenSkipOutputPath=$testRootDir/crossgenlibs.skip.txt
315     fi
316     if [ -f "$libsCrossgenSkipOutputPath" ]; then
317         rm -f "$libsCrossgenSkipOutputPath"
318     fi
319 }
320
321 function text_file_output_add_test {
322     local scriptFilePath=$1
323     local testResult=$2 # Pass, Fail, or Skip
324
325     if [ "$testResult" == "Pass" ]; then
326         echo "$scriptFilePath" >>"$testsPassOutputPath"
327     elif [ "$testResult" == "Skip" ]; then
328         echo "$scriptFilePath" >>"$testsSkipOutputPath"
329     else
330         echo "$scriptFilePath" >>"$testsFailOutputPath"
331     fi
332 }
333
334 function exit_with_error {
335     local errorSource=$1
336     local errorMessage=$2
337     local printUsage=$3
338
339     if [ -z "$printUsage" ]; then
340         ((printUsage = 0))
341     fi
342
343     echo "$errorMessage"
344     xunit_output_end "$errorSource" "$errorMessage"
345     if ((printUsage != 0)); then
346         print_usage
347     fi
348     exit $EXIT_CODE_EXCEPTION
349 }
350
351 # Handle Ctrl-C. We will stop execution and print the results that
352 # we gathered so far.
353 function handle_ctrl_c {
354     local errorSource='handle_ctrl_c'
355
356     echo ""
357     echo "*** Stopping... ***"
358     print_results
359     exit_with_error "$errorSource" "Test run aborted by Ctrl+C."
360 }
361
362 # Register the Ctrl-C handler
363 trap handle_ctrl_c INT
364
365 # Variables for unsupported and failing tests
366 declare -a unsupportedCrossGenLibs
367 declare -a unsupportedCrossGenTests
368 declare -a unsupportedTests
369
370 # Get an array of items by reading the specified file line by line.
371 function read_array {
372     local theArray=()
373
374     if [ ! -f "$1" ]; then
375         return
376     fi
377
378     # bash in Mac OS X doesn't support 'readarray', so using alternate way instead.
379     # readarray -t theArray < "$1"
380     # Any line that starts with '#' is ignored.
381     while IFS='' read -r line || [ -n "$line" ]; do
382         if [[ $line != "#"* ]]; then
383             theArray[${#theArray[@]}]=$line
384         fi
385     done < "$1"
386     echo ${theArray[@]}
387 }
388
389 function load_unsupported_tests {
390     # Load the list of tests that are not supported on this platform
391     unsupportedTests=($(read_array "$(dirname "$0")/unsupportedTests.txt"))
392     # Load the list of libs that are not supported for crossgen on this platform
393     unsupportedCrossGenLibs=($(read_array "$(dirname "$0")/unsupportedCrossgenLibs.txt"))
394     # Load the list of test that are not supported for crossgen on this platform
395     unsupportedCrossGenTests=($(read_array "$(dirname "$0")/unsupportedCrossgenTests.txt"))
396 }
397
398 function is_unsupported_crossgen_lib {
399     for unsupported in "${unsupportedCrossGenLibs[@]}"; do
400         if [ "$1" == "$unsupported" ]; then
401             return 0
402         fi
403     done
404     return 1
405 }
406
407 function is_unsupported_test {
408     for unsupported in "${unsupportedTests[@]}"; do
409         if [ "$1" == "$unsupported" ]; then
410             return 0
411         fi
412     done
413
414     if [ $doCrossgenTests == 1 ] || [ $doCrossgen2Tests == 1 ]; then
415         for unsupported in "${unsupportedCrossGenTests[@]}"; do
416             if [ "$1" == "$unsupported" ]; then
417                 return 0
418             fi
419         done
420     fi
421
422     return 1
423 }
424
425 function crossgen_file {
426     local overlayDir=$1
427     local dll_path=$2
428     local name=$3
429
430     # ni.dll are created as ni.dll.tmp in order to not interfere
431     # with crossgen2 at first, then moved to ni.dll all at once at the end
432     # (logic is similar to https://github.com/dotnet/runtime/pull/57341/)
433     local tmp_ni_path=$(echo $dll_path | sed 's/.dll$/.ni.dll.tmp/')
434
435     echo "Precompiling $dll_path to $tmp_ni_path"
436
437     if [ $doCrossgenLibs == 1 ]; then
438         ${overlayDir}/crossgen /in $dll_path /out $tmp_ni_path $CrossGenOptions /p ${overlayDir}:${overlayDir}/crossgen2 &>$dll_path.out
439     elif [ $doCrossgen2Libs == 1 ]; then
440         ${overlayDir}/corerun ${overlayDir}/crossgen2/crossgen2.dll $CrossGen2Options -o:$tmp_ni_path $dll_path -r:${overlayDir}/*.dll -r:${overlayDir}/crossgen2/*.dll &>$dll_path.out
441     fi
442
443     local exitCode=$?
444     if [[ $exitCode != 0 ]]; then
445         echo Crossgen fail for $dll_path.
446         echo "$dll_path" >> "$libsCrossgenFailOutputPath"
447
448         cat $dll_path.out
449
450         if [ $CrossGenAllowFail == 0 ]; then
451             exit $exitCode
452         fi
453     else
454         echo Crossgen success $dll_path.
455         echo "$dll_path" >> "$libsCrossgenPassOutputPath"
456
457         if [[ "${name}" == "System.Private.CoreLib.dll" ]]; then
458             # SPC.ni.dll can be used directly without ni.dll.tmp
459             # (because SPC doesn't use any dependencies)
460             mv $dll_path $dll_path.bak
461             mv $tmp_ni_path $dll_path
462         fi
463     fi
464
465     rm $dll_path.out
466 }
467
468 function precompile_overlay_assemblies {
469     local overlayDir=$CORE_ROOT
470
471     if [ $doCopyNetcoreToCoreroot == 1 ]; then
472         echo "Copying netcore ($netcoreDir) to coreroot ($overlayDir)"
473         cp -r $netcoreDir/* $overlayDir
474     fi
475
476     echo Cleanup old ni.dll
477     for file in `find $testRootDir -name "*.ni.*"`; do
478         rm $file
479     done
480
481     test -f $overlayDir/System.Private.CoreLib.dll.Backup && mv $overlayDir/System.Private.CoreLib.dll.Backup $overlayDir/System.Private.CoreLib.dll
482     test -f $overlayDir/System.Private.CoreLib.dll.bak && mv $overlayDir/System.Private.CoreLib.dll.bak $overlayDir/System.Private.CoreLib.dll
483
484     if [ $doCrossgenLibs == 0 ] && [ $doCrossgen2Libs == 0 ]; then
485         echo Skipping crossgen of libs.
486         return 0
487     else
488         echo Precompiling libs
489     fi
490
491     if [ $doCrossgenSPCFirst == 1 ]; then
492         local dll_path=$overlayDir/System.Private.CoreLib.dll
493         local name=$(basename $dll_path)
494         crossgen_file $overlayDir $dll_path $name
495     fi
496
497     if [ -z "$crossgenLibs" ]; then
498         # No libs to crossgen were specified, so crossgen everything in the overlay
499         filesToPrecompile=$(find -L $overlayDir -iname \*.dll -not -iname \*.ni.dll -type f)
500     else
501         # Otherwise, compile only specified libs
502         filesToPrecompile=$(echo "${crossgenLibs[@]}")
503     fi
504
505     for fileToPrecompile in ${filesToPrecompile}
506     do
507         local dll_path=${fileToPrecompile}
508         local name=$(basename $dll_path)
509
510         if [[ "$name" == "System.Private.CoreLib.dll" && "$doCrossgenSPCFirst" == "1" ]]; then
511             continue
512         fi
513
514         if is_unsupported_crossgen_lib "${name}"; then
515             echo Skipping crossgen for $dll_path.
516             echo "$dll_path" >> "$libsCrossgenSkipOutputPath"
517             continue
518         fi
519
520         crossgen_file $overlayDir $dll_path $name
521     done
522
523     # Copy ni.dll all at once
524     for tmp_ni_path in `find $overlayDir -name "*.ni.dll.tmp"`; do
525         local ni_dll_path=$(echo $tmp_ni_path | sed 's/.ni.dll.tmp$/.ni.dll/')
526
527         echo "Moving $tmp_ni_path to $ni_dll_path"
528         mv $tmp_ni_path $ni_dll_path
529     done
530 }
531
532 function skip_unsupported_test {
533     # This function runs in a background process. It should not echo anything, and should not use global variables. This
534     # function is analogous to run_test, and causes the test to be skipped with the message below.
535
536     local scriptFilePath=$1
537     local outputFilePath=$2
538
539     echo "Not supported on this platform or in this mode." >"$outputFilePath"
540     return 2 # skip the test
541 }
542
543 function run_test {
544     # This function runs in a background process. It should not echo anything, and should not use global variables.
545
546     local scriptFilePath=$1
547     local outputFilePath=$2
548
549     # Switch to directory where the script is
550     cd "$(dirname "$scriptFilePath")"
551
552     local scriptFileName=$(basename "$scriptFilePath")
553     local outputFileName=$(basename "$outputFilePath")
554
555     echo "" > "$outputFileName" 2>&1
556
557     if [ $useMulticoreJit == 1 ]; then
558       export COMPlus_MultiCoreJitProfile="`pwd`/mcj_profile.dat"
559       export COMPlus_MultiCoreJitMinNumCpus="1"
560       echo "MULTICOREJIT: 1st launch" >> "$outputFileName" 2>&1
561
562       "./$scriptFileName" >> "$outputFileName" 2>&1
563       local testScriptExitCode=$?
564
565       if [ $testScriptExitCode == 0 ]; then
566         if [ $useMulticoreJitNoProfileGather == 1 ]; then
567           export COMPlus_MultiCoreJitNoProfileGather="1"
568         fi
569
570         echo "" >> "$outputFileName" 2>&1
571         echo "MULTICOREJIT: 2nd launch" >> "$outputFileName" 2>&1
572       else
573         return $testScriptExitCode
574       fi
575     fi
576
577     if [ $doCrossgenTests == 1 ]; then
578       export RunCrossGen=1
579     fi
580
581     if [ $doCrossgen2Tests == 1 ]; then
582       export RunCrossGen2=1
583     fi
584
585     "./$scriptFileName" >> "$outputFileName" 2>&1
586     local testScriptExitCode=$?
587
588     return $testScriptExitCode
589 }
590
591 # Variables for running tests in the background
592 ((maxProcesses = 1)) # long tests delay process creation, use a few more processors
593
594 ((processCount = 0))
595 declare -a scriptFilePaths
596 declare -a outputFilePaths
597 declare -a processIds
598 declare -a testStartTimes
599 waitProcessIndex=
600 pidNone=0
601
602 function waitany {
603     local pid
604     local exitcode
605     while true; do
606         for (( i=0; i<$maxProcesses; i++ )); do
607             pid=${processIds[$i]}
608             if [ -z "$pid" ] || [ "$pid" == "$pidNone" ]; then
609                 continue
610             fi
611             if ! kill -0 $pid 2>/dev/null; then
612                 wait $pid
613                 exitcode=$?
614                 waitProcessIndex=$i
615                 processIds[$i]=$pidNone
616                 return $exitcode
617             fi
618         done
619         sleep 0.1
620     done
621 }
622
623 function get_available_process_index {
624     local pid
625     local i=0
626     for (( i=0; i<$maxProcesses; i++ )); do
627         pid=${processIds[$i]}
628         if [ -z "$pid" ] || [ "$pid" == "$pidNone" ]; then
629             break
630         fi
631     done
632     echo $i
633 }
634
635 function finish_test {
636     waitany
637     local testScriptExitCode=$?
638     local finishedProcessIndex=$waitProcessIndex
639     ((--processCount))
640
641     local scriptFilePath=${scriptFilePaths[$finishedProcessIndex]}
642     local outputFilePath=${outputFilePaths[$finishedProcessIndex]}
643     local scriptFileName=$(basename "$scriptFilePath")
644
645     local testEndTime=
646     local testRunningTime=
647     local header=
648
649     if ((verbose == 1)); then
650         header=$(printf "[%4d]" $countTotalTests)
651     fi
652
653     testEndTime=$(date +%s)
654     testRunningTime=$(( $testEndTime - ${testStartTimes[$finishedProcessIndex]} ))
655     header=$header$(printf "[%4ds]" $testRunningTime)
656
657     if [ $useMulticoreJit == 1 ]; then
658       header="$header[mcj]"
659     fi
660
661     local testResult
662     case $testScriptExitCode in
663         0)
664             let countPassedTests++
665             testResult='Pass'
666             if ((verbose == 1)); then
667                 echo "PASSED   - ${header}${scriptFilePath}"
668             else
669                 echo "         - ${header}${scriptFilePath}"
670             fi
671             ;;
672         2)
673             let countSkippedTests++
674             testResult='Skip'
675             echo "SKIPPED  - ${header}${scriptFilePath}"
676             ;;
677         *)
678             let countFailedTests++
679             testResult='Fail'
680             echo "FAILED   - ${header}${scriptFilePath}"
681             ;;
682     esac
683     let countTotalTests++
684
685     if ((verbose == 1 || testScriptExitCode != 0)); then
686         while IFS='' read -r line || [ -n "$line" ]; do
687             echo "               $line"
688         done <"$outputFilePath"
689     fi
690
691     xunit_output_add_test "$scriptFilePath" "$outputFilePath" "$testResult" "$testScriptExitCode" "$testRunningTime"
692     text_file_output_add_test "$scriptFilePath" "$testResult"
693 }
694
695 function finish_remaining_tests {
696     # Finish the remaining tests in the order in which they were started
697     while ((processCount > 0)); do
698         finish_test
699     done
700 }
701
702 function prep_test {
703     local scriptFilePath=$1
704     local scriptFileDir=$(dirname "$scriptFilePath")
705
706     test "$verbose" == 1 && echo "Preparing $scriptFilePath"
707
708     # Add executable file mode bit if needed
709     chmod +x "$scriptFilePath"
710
711     # remove any Locks
712     rm -rf $scriptFileDir/lock
713
714     # remove mcj profiles
715     rm -rf $scriptFileDir/mcj_profile.dat*
716 }
717
718 function start_test {
719     local nextProcessIndex=$(get_available_process_index)
720     local scriptFilePath=$1
721
722     if ((nextProcessIndex == maxProcesses)); then
723         finish_test
724         nextProcessIndex=$(get_available_process_index)
725     fi
726
727     scriptFilePaths[$nextProcessIndex]=$scriptFilePath
728     local scriptFileName=$(basename "$scriptFilePath")
729     local outputFilePath=$(dirname "$scriptFilePath")/${scriptFileName}.out
730     outputFilePaths[$nextProcessIndex]=$outputFilePath
731
732     testStartTimes[$nextProcessIndex]=$(date +%s)
733
734     test "$verbose" == 1 && echo "Starting $scriptFilePath"
735     if is_unsupported_test "$scriptFilePath"; then
736         skip_unsupported_test "$scriptFilePath" "$outputFilePath" &
737     else
738         run_test "$scriptFilePath" "$outputFilePath" &
739     fi
740     processIds[$nextProcessIndex]=$!
741
742     ((++processCount))
743 }
744
745 # Get a list of directories in which to scan for tests by reading the
746 # specified file line by line.
747 function set_test_directories {
748     local errorSource='set_test_directories'
749
750     local listFileName=$1
751
752     if [ ! -f "$listFileName" ]
753     then
754         exit_with_error "$errorSource" "Test directories file not found at $listFileName"
755     fi
756     testDirectories=($(read_array "$listFileName"))
757 }
758
759 function run_tests_in_directory {
760     local testDir=$1
761     local skipScriptsInDir=$2
762
763     local minDepth="1"
764
765     # Need to skip scripts in testDir since they are not tests
766     if [[ "$skipScriptsInDir" == "1" ]]; then
767         minDepth="2"
768     fi
769
770     # Recursively search through directories for .sh files to prepare them.
771     # Note: This needs to occur before any test runs as some of the .sh files
772     # depend on other .sh files
773     echo "Preparing tests..."
774     for scriptFilePath in $(find "$testDir" -mindepth ${minDepth} -type f -iname '*.sh' | sort)
775     do
776         prep_test "${scriptFilePath:2}"
777     done
778     echo "The tests have been prepared"
779     # Recursively search through directories for .sh files to run.
780     for scriptFilePath in $(find "$testDir" -mindepth ${minDepth} -type f -iname '*.sh' | sort)
781     do
782         start_test "${scriptFilePath:2}"
783     done
784 }
785
786 # Exit code constants
787 readonly EXIT_CODE_SUCCESS=0       # Script ran normally.
788 readonly EXIT_CODE_EXCEPTION=1     # Script exited because something exceptional happened (e.g. bad arguments, Ctrl-C interrupt).
789 readonly EXIT_CODE_TEST_FAILURE=2  # Script completed successfully, but one or more tests failed.
790
791 # Argument variables
792 testRootDir="/opt/usr/coreclr-tc/"
793 coreOverlayDir="/opt/usr/coreclr-tc/coreroot/"
794 doCopyNetcoreToCoreroot=0
795 netcoreDir="/usr/share/dotnet.tizen/netcoreapp"
796 testEnv=
797
798 # Handle arguments
799 verbose=0
800 doCrossgenLibs=0
801 doCrossgen2Libs=0
802 CrossGenAllowFail=0
803 doCrossgenTests=0
804 doCrossgen2Tests=0
805 doCrossgenSPCFirst=0
806
807 useMulticoreJit=0
808 useMulticoreJitNoProfileGather=0
809
810 crossgenOpts=""
811 crossgen2Opts=""
812
813 for i in "$@"
814 do
815     case $i in
816         -h|--help)
817             print_usage
818             exit $EXIT_CODE_SUCCESS
819             ;;
820         -v|--verbose)
821             verbose=1
822             ;;
823         --arch=*)
824             ARCH=${i#*=}
825             ;;
826         --crossgen-libs)
827             doCrossgenLibs=1
828             ;;
829         --crossgen2-libs)
830             doCrossgen2Libs=1
831             ;;
832         --crossgen-tests)
833             doCrossgenTests=1
834             ;;
835         --crossgen2-tests)
836             doCrossgen2Tests=1
837             ;;
838         --crossgen-spc-first)
839             doCrossgenSPCFirst=1
840             ;;
841         --crossgenoptions=*)
842             crossgenOpts="$crossgenOpts ${i#*=}"
843             ;;
844         --crossgen2options=*)
845             crossgen2Opts="$crossgen2Opts ${i#*=}"
846             ;;
847         --allow-crossgen-fails)
848             CrossGenAllowFail=1
849             ;;
850         --crossgen-lib=*)
851             crossgenLibs[${#crossgenLibs[@]}]=${i#*=}
852             ;;
853         --jitstress=*)
854             export COMPlus_JitStress=${i#*=}
855             ;;
856         --jitstressregs=*)
857             export COMPlus_JitStressRegs=${i#*=}
858             ;;
859         --mcj)
860             useMulticoreJit=1
861             ;;
862         --mcj-no-profile-gather)
863             useMulticoreJitNoProfileGather=1
864             ;;
865         --testRootDir=*)
866             testRootDir=${i#*=}
867             ;;
868         --coreOverlayDir=*)
869             coreOverlayDir=${i#*=}
870             ;;
871         --netcoreDir=*)
872             netcoreDir=${i#*=}
873             ;;
874         --copy-netcore-to-coreroot)
875             doCopyNetcoreToCoreroot=1
876             ;;
877         --testDir=*)
878             testDirectories[${#testDirectories[@]}]=${i#*=}
879             ;;
880         --testDirFile=*)
881             set_test_directories "${i#*=}"
882             ;;
883         --num-procs=*)
884             ((maxProcesses = ${i#*=}))
885             ;;
886         --test-env=*)
887             testEnv=${i#*=}
888             ;;
889         --gcstresslevel=*)
890             export COMPlus_GCStress=${i#*=}
891             ;;
892         --xunitOutputPath=*)
893             xunitOutputPath=${i#*=}
894             ;;
895         --testsPassOutputPath=*)
896             testsPassOutputPath=${i#*=}
897             ;;
898         --testsSkipOutputPath=*)
899             testsSkipOutputPath=${i#*=}
900             ;;
901         --testsFailOutputPath=*)
902             testsFailOutputPath=${i#*=}
903             ;;
904         --libsCrossgenPassOutputPath=*)
905             libsCrossgenPassOutputPath=${i#*=}
906             ;;
907         --libsCrossgenSkipOutputPath=*)
908             libsCrossgenSkipOutputPath=${i#*=}
909             ;;
910         --libsCrossgenFailOutputPath=*)
911             libsCrossgenFailOutputPath=${i#*=}
912             ;;
913         *)
914             echo "Unknown switch: $i"
915             print_usage
916             exit $EXIT_CODE_SUCCESS
917             ;;
918     esac
919 done
920
921 if [ -z "$ARCH" ]; then
922     echo "--arch is required."
923     print_usage
924     exit $EXIT_CODE_EXCEPTION
925 fi
926 if [ "$ARCH" != "arm" ] && [ "$ARCH" != "armel" ] && [ "$ARCH" != "arm64" ] && [ "$ARCH" != "x64" ] && [ "$ARCH" != "x86" ] && [ "$ARCH" != "riscv64" ]; then
927     echo "Unsupported value for --arch: $ARCH"
928     print_usage
929     exit $EXIT_CODE_EXCEPTION
930 fi
931
932 if [ -z "$testRootDir" ]; then
933     echo "--testRootDir is required."
934     print_usage
935     exit $EXIT_CODE_EXCEPTION
936 fi
937 if [ ! -d "$testRootDir" ]; then
938     echo "Directory specified by --testRootDir does not exist: $testRootDir"
939     exit $EXIT_CODE_EXCEPTION
940 fi
941
942 if [ -z "$coreOverlayDir" ]; then
943     echo "--coreOverlayDir is required."
944     print_usage
945     exit $EXIT_CODE_EXCEPTION
946 fi
947 if [ ! -d "$coreOverlayDir" ]; then
948     echo "Directory specified by --coreOverlayDir does not exist: $coreOverlayDir"
949     exit $EXIT_CODE_EXCEPTION
950 fi
951
952 export CORE_ROOT="$coreOverlayDir"
953
954 # Default values for crossgen2 options
955 if [ "$crossgen2Opts" == "" ]; then
956     crossgen2Opts="--jitpath $coreOverlayDir/libclrjit.so --targetarch $ARCH"
957 fi
958
959 export CrossGenOptions="$crossgenOpts"
960 export CrossGen2Options="$crossgen2Opts"
961 export CrossGen2OptionsR2RTest="--crossgen2-jitpath $coreOverlayDir/libclrjit.so --target-arch $ARCH"
962
963 echo "! Make sure CLR/FX are copied to $coreOverlayDir !"
964
965 echo "Running on CPU-$ARCH"
966
967 xunit_output_begin
968 text_file_output_begin
969 load_unsupported_tests
970 precompile_overlay_assemblies
971
972 export __TestEnv=$testEnv
973 cd "$testRootDir"
974
975 time_start=$(date +"%s")
976 if [ -z "$testDirectories" ]
977 then
978     # No test directories were specified, so run everything in the current
979     # directory and its subdirectories.
980     run_tests_in_directory "." 1
981 else
982     # Otherwise, run all the tests in each specified test directory.
983     for testDir in "${testDirectories[@]}"
984     do
985         if [ ! -d "$testDir" ]; then
986             echo "Test directory does not exist: $testDir"
987         else
988             run_tests_in_directory "./$testDir" 0
989         fi
990     done
991 fi
992
993 finish_remaining_tests
994 print_results
995
996 time_end=$(date +"%s")
997 time_diff=$(($time_end-$time_start))
998 echo "$(($time_diff / 60)) minutes and $(($time_diff % 60)) seconds taken to run CoreCLR tests."
999
1000 xunit_output_end
1001
1002 if ((countFailedTests > 0)); then
1003     exit $EXIT_CODE_TEST_FAILURE
1004 fi
1005
1006 exit $EXIT_CODE_SUCCESS