Merge pull request #17161 from dotnetrt/testbuild
[platform/upstream/coreclr.git] / tests / runtest.sh
1 #!/usr/bin/env bash
2
3 function print_usage {
4     echo ''
5     echo 'CoreCLR test runner script.'
6     echo ''
7     echo 'Typical command line:'
8     echo ''
9     echo 'coreclr/tests/runtest.sh'
10     echo '    --testRootDir="temp/Windows_NT.x64.Debug"'
11     echo '    --testNativeBinDir="coreclr/bin/obj/Linux.x64.Debug/tests"'
12     echo '    --coreOverlayDir="coreclr/bin/tests/Linux.x64.Debug/Tests/Core_Root"'
13     echo '    --copyNativeTestBin'
14     echo ''
15     echo 'Required arguments:'
16     echo '  --testRootDir=<path>             : Root directory of the test build (e.g. coreclr/bin/tests/Windows_NT.x64.Debug).'
17     echo '  --testNativeBinDir=<path>        : Directory of the native CoreCLR test build (e.g. coreclr/bin/obj/Linux.x64.Debug/tests).'
18     echo '  (Also required: Either --coreOverlayDir, or all of the switches --coreOverlayDir overrides)'
19     echo ''
20     echo 'Optional arguments:'
21     echo '  --coreOverlayDir=<path>          : Directory containing core binaries and test dependencies. If not specified, the'
22     echo '                                     default is testRootDir/Tests/coreoverlay. This switch overrides --coreClrBinDir,'
23     echo '                                     --mscorlibDir, and --coreFxBinDir.'
24     echo '  --coreClrBinDir=<path>           : Directory of the CoreCLR build (e.g. coreclr/bin/Product/Linux.x64.Debug).'
25     echo '  --mscorlibDir=<path>             : Directory containing the built mscorlib.dll. If not specified, it is expected to be'
26     echo '                                       in the directory specified by --coreClrBinDir.'
27     echo '  --coreFxBinDir="<path>"          : Directory with CoreFX build outputs'
28     echo '                                     (e.g. "corefx/bin/runtime/netcoreapp-Linux-Debug-x64")'
29     echo '                                     If files with the same name are present in multiple directories, the first one wins.'
30     echo '  --testDir=<path>                 : Run tests only in the specified directory. The path is relative to the directory'
31     echo '                                     specified by --testRootDir. Multiple of this switch may be specified.'
32     echo '  --testDirFile=<path>             : Run tests only in the directories specified by the file at <path>. Paths are listed'
33     echo '                                     one line, relative to the directory specified by --testRootDir.'
34     echo '  --build-overlay-only             : Build coreoverlay only, and skip running tests.'
35     echo '  --runFailingTestsOnly            : Run only the tests that are disabled on this platform due to unexpected failures.'
36     echo '                                     Failing tests are listed in coreclr/tests/failingTestsOutsideWindows.txt, one per'
37     echo '                                     line, as paths to .sh files relative to the directory specified by --testRootDir.'
38     echo '  --disableEventLogging            : Disable the events logged by both VM and Managed Code'
39     echo '  --sequential                     : Run tests sequentially (default is to run in parallel).'
40     echo '  --playlist=<path>                : Run only the tests that are specified in the file at <path>, in the same format as'
41     echo '                                     runFailingTestsOnly'
42     echo '  -v, --verbose                    : Show output from each test.'
43     echo '  -h|--help                        : Show usage information.'
44     echo '  --useServerGC                    : Enable server GC for this test run'
45     echo '  --test-env                       : Script to set environment variables for tests'
46     echo '  --copyNativeTestBin              : Explicitly copy native test components into the test dir'
47     echo '  --crossgen                       : Precompiles the framework managed assemblies'
48     echo '  --runcrossgentests               : Runs the ready to run tests' 
49     echo '  --jitstress=<n>                  : Runs the tests with COMPlus_JitStress=n'
50     echo '  --jitstressregs=<n>              : Runs the tests with COMPlus_JitStressRegs=n'
51     echo '  --jitminopts                     : Runs the tests with COMPlus_JITMinOpts=1'
52     echo '  --jitforcerelocs                 : Runs the tests with COMPlus_ForceRelocs=1'
53     echo '  --jitdisasm                      : Runs jit-dasm on the tests'
54     echo '  --gcstresslevel=<n>              : Runs the tests with COMPlus_GCStress=n'
55     echo '  --gcname=<n>                     : Runs the tests with COMPlus_GCName=n'
56     echo '  --ilasmroundtrip                 : Runs ilasm round trip on the tests'
57     echo '    0: None                                1: GC on all allocs and '"'easy'"' places'
58     echo '    2: GC on transitions to preemptive GC  4: GC on every allowable JITed instr'
59     echo '    8: GC on every allowable NGEN instr   16: GC only on a unique stack trace'
60     echo '  --long-gc                        : Runs the long GC tests'
61     echo '  --gcsimulator                    : Runs the GCSimulator tests'
62     echo '  --tieredcompilation              : Runs the tests with COMPlus_EXPERIMENTAL_TieredCompilation=1'
63     echo '  --link <ILlink>                  : Runs the tests after linking via ILlink'
64     echo '  --show-time                      : Print execution sequence and running time for each test'
65     echo '  --no-lf-conversion               : Do not execute LF conversion before running test script'
66     echo '  --limitedDumpGeneration          : Enables the generation of a limited number of core dumps if test(s) crash, even if ulimit'
67     echo '                                     is zero when launching this script. This option is intended for use in CI.'
68     echo '  --xunitOutputPath=<path>         : Create xUnit XML report at the specifed path (default: <test root>/coreclrtests.xml)'
69     echo ''
70     echo 'Runtime Code Coverage options:'
71     echo '  --coreclr-coverage               : Optional argument to get coreclr code coverage reports'
72     echo '  --coreclr-objs=<path>            : Location of root of the object directory'
73     echo '                                     containing the linux/mac coreclr build'
74     echo '  --coreclr-src=<path>             : Location of root of the directory'
75     echo '                                     containing the coreclr source files'
76     echo '  --coverage-output-dir=<path>     : Directory where coverage output will be written to'
77     echo ''
78 }
79
80 function print_results {
81     echo ""
82     echo "======================="
83     echo "     Test Results"
84     echo "======================="
85     echo "# CoreCLR Bin Dir  : $coreClrBinDir"
86     echo "# Tests Discovered : $countTotalTests"
87     echo "# Passed           : $countPassedTests"
88     echo "# Failed           : $countFailedTests"
89     echo "# Skipped          : $countSkippedTests"
90     echo "======================="
91 }
92
93 # Initialize counters for bookkeeping.
94 countTotalTests=0
95 countPassedTests=0
96 countFailedTests=0
97 countSkippedTests=0
98
99 # Variables for xUnit-style XML output. XML format: https://xunit.github.io/docs/format-xml-v2.html
100 xunitOutputPath=
101 xunitTestOutputPath=
102
103 # Variables for text file output. These can be passed back to runtest.sh using the "--playlist" argument
104 # to rerun specific tests.
105 testsPassOutputPath=
106 testsFailOutputPath=
107 testsSkipOutputPath=
108
109 # libExtension determines extension for dynamic library files
110 # runtimeName determines where CoreFX Runtime files will be located
111 OSName=$(uname -s)
112 libExtension=
113 case $OSName in
114     Darwin)
115         libExtension="dylib"
116         ;;
117
118     Linux)
119         libExtension="so"
120         ;;
121
122     NetBSD)
123         libExtension="so"
124         ;;
125
126     *)
127         echo "Unsupported OS $OSName detected, configuring as if for Linux"
128         libExtension="so"
129         ;;
130 esac
131
132 function xunit_output_begin {
133     if [ -z "$xunitOutputPath" ]; then
134         xunitOutputPath=$testRootDir/coreclrtests.xml
135     fi
136     if ! [ -e $(basename "$xunitOutputPath") ]; then
137         xunitOutputPath=$testRootDir/coreclrtests.xml
138     fi
139     xunitTestOutputPath=${xunitOutputPath}.test
140     if [ -e "$xunitOutputPath" ]; then
141         rm -f -r "$xunitOutputPath"
142     fi
143     if [ -e "$xunitTestOutputPath" ]; then
144         rm -f -r "$xunitTestOutputPath"
145     fi
146 }
147
148 function xunit_output_add_test {
149     # <assemblies>
150     #   <assembly>
151     #     <collection>
152     #       <test .../> <!-- Write this element here -->
153
154     local scriptFilePath=$1
155     local outputFilePath=$2
156     local testResult=$3 # Pass, Fail, or Skip
157     local testScriptExitCode=$4
158     local testRunningTime=$5
159
160     local testPath=${scriptFilePath%.sh} # Remove trailing ".sh"
161     local testDir=$(dirname "$testPath")
162     local testName=$(basename "$testPath")
163
164     # Replace '/' with '.'
165     testPath=$(echo "$testPath" | tr / .)
166     testDir=$(echo "$testDir" | tr / .)
167
168     local line
169
170     line="      "
171     line="${line}<test"
172     line="${line} name=\"${testPath}\""
173     line="${line} type=\"${testDir}\""
174     line="${line} method=\"${testName}\""
175     line="${line} result=\"${testResult}\""
176     if [ -n "$testRunningTime" ] && [ "$testResult" != "Skip" ]; then
177         line="${line} time=\"${testRunningTime}\""
178     fi
179
180     if [ "$testResult" == "Pass" ]; then
181         line="${line}/>"
182         echo "$line" >>"$xunitTestOutputPath"
183         return
184     fi
185
186     line="${line}>"
187     echo "$line" >>"$xunitTestOutputPath"
188
189     line="        "
190     if [ "$testResult" == "Skip" ]; then
191         line="${line}<reason><![CDATA[$(cat "$outputFilePath")]]></reason>"
192         echo "$line" >>"$xunitTestOutputPath"
193     else
194         line="${line}<failure exception-type=\"Exit code: ${testScriptExitCode}\">"
195         echo "$line" >>"$xunitTestOutputPath"
196
197         line="          "
198         line="${line}<message>"
199         echo "$line" >>"$xunitTestOutputPath"
200         line="            "
201         line="${line}<![CDATA["
202         echo "$line" >>"$xunitTestOutputPath"
203         cat "$outputFilePath" >>"$xunitTestOutputPath"
204         line="            "
205         line="${line}]]>"
206         echo "$line" >>"$xunitTestOutputPath"
207         line="          "
208         line="${line}</message>"
209         echo "$line" >>"$xunitTestOutputPath"
210
211         line="        "
212         line="${line}</failure>"
213         echo "$line" >>"$xunitTestOutputPath"
214     fi
215
216     line="      "
217     line="${line}</test>"
218     echo "$line" >>"$xunitTestOutputPath"
219 }
220
221 function xunit_output_end {
222     local errorSource=$1
223     local errorMessage=$2
224
225     local errorCount
226     if [ -z "$errorSource" ]; then
227         ((errorCount = 0))
228     else
229         ((errorCount = 1))
230     fi
231
232     echo '<?xml version="1.0" encoding="utf-8"?>' >>"$xunitOutputPath"
233     echo '<assemblies>' >>"$xunitOutputPath"
234
235     local line
236
237     # <assembly ...>
238     line="  "
239     line="${line}<assembly"
240     line="${line} name=\"CoreClrTestAssembly\""
241     line="${line} total=\"${countTotalTests}\""
242     line="${line} passed=\"${countPassedTests}\""
243     line="${line} failed=\"${countFailedTests}\""
244     line="${line} skipped=\"${countSkippedTests}\""
245     line="${line} errors=\"${errorCount}\""
246     line="${line}>"
247     echo "$line" >>"$xunitOutputPath"
248
249     # <collection ...>
250     line="    "
251     line="${line}<collection"
252     line="${line} name=\"CoreClrTestCollection\""
253     line="${line} total=\"${countTotalTests}\""
254     line="${line} passed=\"${countPassedTests}\""
255     line="${line} failed=\"${countFailedTests}\""
256     line="${line} skipped=\"${countSkippedTests}\""
257     line="${line}>"
258     echo "$line" >>"$xunitOutputPath"
259
260     # <test .../> <test .../> ...
261     if [ -f "$xunitTestOutputPath" ]; then
262         cat "$xunitTestOutputPath" >>"$xunitOutputPath"
263         rm -f "$xunitTestOutputPath"
264     fi
265
266     # </collection>
267     line="    "
268     line="${line}</collection>"
269     echo "$line" >>"$xunitOutputPath"
270
271     if [ -n "$errorSource" ]; then
272         # <errors>
273         line="    "
274         line="${line}<errors>"
275         echo "$line" >>"$xunitOutputPath"
276
277         # <error ...>
278         line="      "
279         line="${line}<error"
280         line="${line} type=\"TestHarnessError\""
281         line="${line} name=\"${errorSource}\""
282         line="${line}>"
283         echo "$line" >>"$xunitOutputPath"
284
285         # <failure .../>
286         line="        "
287         line="${line}<failure>${errorMessage}</failure>"
288         echo "$line" >>"$xunitOutputPath"
289
290         # </error>
291         line="      "
292         line="${line}</error>"
293         echo "$line" >>"$xunitOutputPath"
294
295         # </errors>
296         line="    "
297         line="${line}</errors>"
298         echo "$line" >>"$xunitOutputPath"
299     fi
300
301     # </assembly>
302     line="  "
303     line="${line}</assembly>"
304     echo "$line" >>"$xunitOutputPath"
305
306     # </assemblies>
307     echo '</assemblies>' >>"$xunitOutputPath"
308 }
309
310 function text_file_output_begin {
311     if [ -z "$testsPassOutputPath" ]; then
312         testsPassOutputPath=$testRootDir/coreclrtests.pass.txt
313     fi
314     if ! [ -e $(basename "$testsPassOutputPath") ]; then
315         testsPassOutputPath=$testRootDir/coreclrtests.pass.txt
316     fi
317     if [ -e "$testsPassOutputPath" ]; then
318         rm -f "$testsPassOutputPath"
319     fi
320     if [ -z "$testsFailOutputPath" ]; then
321         testsFailOutputPath=$testRootDir/coreclrtests.fail.txt
322     fi
323     if ! [ -e $(basename "$testsFailOutputPath") ]; then
324         testsFailOutputPath=$testRootDir/coreclrtests.fail.txt
325     fi
326     if [ -e "$testsFailOutputPath" ]; then
327         rm -f "$testsFailOutputPath"
328     fi
329     if [ -z "$testsSkipOutputPath" ]; then
330         testsSkipOutputPath=$testRootDir/coreclrtests.skip.txt
331     fi
332     if ! [ -e $(basename "$testsSkipOutputPath") ]; then
333         testsSkipOutputPath=$testRootDir/coreclrtests.skip.txt
334     fi
335     if [ -e "$testsSkipOutputPath" ]; then
336         rm -f "$testsSkipOutputPath"
337     fi
338 }
339
340 function text_file_output_add_test {
341     local scriptFilePath=$1
342     local testResult=$2 # Pass, Fail, or Skip
343
344     if [ "$testResult" == "Pass" ]; then
345         echo "$scriptFilePath" >>"$testsPassOutputPath"
346     elif [ "$testResult" == "Skip" ]; then
347         echo "$scriptFilePath" >>"$testsSkipOutputPath"
348     else
349         echo "$scriptFilePath" >>"$testsFailOutputPath"
350     fi
351 }
352
353 function exit_with_error {
354     local errorSource=$1
355     local errorMessage=$2
356     local printUsage=$3
357
358     if [ -z "$printUsage" ]; then
359         ((printUsage = 0))
360     fi
361
362     echo "$errorMessage"
363     xunit_output_end "$errorSource" "$errorMessage"
364     if ((printUsage != 0)); then
365         print_usage
366     fi
367     exit $EXIT_CODE_EXCEPTION
368 }
369
370 # Handle Ctrl-C. We will stop execution and print the results that
371 # we gathered so far.
372 function handle_ctrl_c {
373     local errorSource='handle_ctrl_c'
374
375     echo ""
376     echo "*** Stopping... ***"
377     print_results
378     exit_with_error "$errorSource" "Test run aborted by Ctrl+C."
379 }
380
381 # Register the Ctrl-C handler
382 trap handle_ctrl_c INT
383
384 function create_core_overlay {
385     local errorSource='create_core_overlay'
386     local printUsage=1
387
388     if [ -n "$coreOverlayDir" ]; then
389         export CORE_ROOT="$coreOverlayDir"
390
391         if [ -n "$copyNativeTestBin" ]; then
392             copy_test_native_bin_to_test_root $coreOverlayDir
393         fi
394
395         return
396     fi
397
398     # Check inputs to make sure we have enough information to create the core layout. $testRootDir/Tests/Core_Root should
399     # already exist and contain test dependencies that are not built.
400     local testDependenciesDir=$testRootDir/Tests/Core_Root
401     if [ ! -d "$testDependenciesDir" ]; then
402         exit_with_error "$errorSource" "Did not find the test dependencies directory: $testDependenciesDir"
403     fi
404     if [ -z "$coreClrBinDir" ]; then
405         exit_with_error "$errorSource" "One of --coreOverlayDir or --coreClrBinDir must be specified." "$printUsage"
406     fi
407     if [ ! -d "$coreClrBinDir" ]; then
408         exit_with_error "$errorSource" "Directory specified by --coreClrBinDir does not exist: $coreClrBinDir"
409     fi
410     if [ -z "$coreFxBinDir" ]; then
411         exit_with_error "$errorSource" "One of --coreOverlayDir or --coreFxBinDir must be specified." "$printUsage"
412     fi
413
414     # Create the overlay
415     coreOverlayDir=$testRootDir/Tests/coreoverlay
416     export CORE_ROOT="$coreOverlayDir"
417     if [ -e "$coreOverlayDir" ]; then
418         rm -f -r "$coreOverlayDir"
419     fi
420     mkdir "$coreOverlayDir"
421
422     cp -f -v "$coreFxBinDir/"* "$coreOverlayDir/" 2>/dev/null
423     cp -f -p -v "$coreClrBinDir/"* "$coreOverlayDir/" 2>/dev/null
424     if [ -d "$mscorlibDir/bin" ]; then
425         cp -f -v "$mscorlibDir/bin/"* "$coreOverlayDir/" 2>/dev/null
426     fi
427     cp -f -v "$testDependenciesDir/"xunit* "$coreOverlayDir/" 2>/dev/null
428     cp -n -v "$testDependenciesDir/"* "$coreOverlayDir/" 2>/dev/null
429     if [ -f "$coreOverlayDir/mscorlib.ni.dll" ]; then
430         # Test dependencies come from a Windows build, and mscorlib.ni.dll would be the one from Windows
431         rm -f "$coreOverlayDir/mscorlib.ni.dll"
432     fi
433     if [ -f "$coreOverlayDir/System.Private.CoreLib.ni.dll" ]; then
434         # Test dependencies come from a Windows build, and System.Private.CoreLib.ni.dll would be the one from Windows
435         rm -f "$coreOverlayDir/System.Private.CoreLib.ni.dll"
436     fi
437     copy_test_native_bin_to_test_root $coreOverlayDir
438 }
439
440 declare -a skipCrossGenFiles
441
442 function is_skip_crossgen_test {
443     for skip in "${skipCrossGenFiles[@]}"; do
444         if [ "$1" == "$skip" ]; then
445             return 0
446         fi
447     done
448     return 1
449 }
450
451 function precompile_overlay_assemblies {
452     skipCrossGenFiles=($(read_array "$(dirname "$0")/skipCrossGenFiles.$ARCH.txt"))
453
454     if [ $doCrossgen == 1 ]; then
455         local overlayDir=$CORE_ROOT
456
457         filesToPrecompile=$(find -L $overlayDir -iname \*.dll -not -iname \*.ni.dll -not -iname \*-ms-win-\* -type f )
458         for fileToPrecompile in ${filesToPrecompile}
459         do
460             local filename=${fileToPrecompile}
461             if [ $jitdisasm == 1 ]; then
462                 $overlayDir/corerun $overlayDir/jit-dasm.dll --crossgen $overlayDir/crossgen --platform $overlayDir --output $testRootDir/dasm $filename
463                 local exitCode=$?
464                 if [ $exitCode != 0 ]; then
465                     echo Unable to generate dasm for $filename
466                 fi
467             else
468                 if is_skip_crossgen_test "$(basename $filename)"; then
469                     continue
470                 fi
471                 echo Precompiling $filename
472                 $overlayDir/crossgen /Platform_Assemblies_Paths $overlayDir $filename 1> $filename.stdout 2>$filename.stderr
473                 local exitCode=$?
474                 if [[ $exitCode != 0 ]]; then
475                     if grep -q -e '0x80131018' $filename.stderr; then
476                         printf "\n\t$filename is not a managed assembly.\n\n"
477                     else
478                         echo Unable to precompile $filename.
479                         cat $filename.stdout
480                         cat $filename.stderr
481                         exit $exitCode
482                     fi
483                 else
484                     rm $filename.{stdout,stderr}
485                 fi
486             fi
487         done
488     else
489         echo Skipping crossgen of FX assemblies.
490     fi
491 }
492
493 function copy_test_native_bin_to_test_root {
494     local errorSource='copy_test_native_bin_to_test_root'
495     local coreRootDir=$1
496
497     if [ -z "$testNativeBinDir" ]; then
498         exit_with_error "$errorSource" "--testNativeBinDir is required."
499     fi
500     testNativeBinDir=$testNativeBinDir/src
501     if [ ! -d "$testNativeBinDir" ]; then
502         exit_with_error "$errorSource" "Directory specified by --testNativeBinDir does not exist: $testNativeBinDir"
503     fi
504
505     # Copy native test components from the native test build into the respective test directory in the test root directory
506     find "$testNativeBinDir" -type f -iname "*.$libExtension" |
507         while IFS='' read -r filePath || [ -n "$filePath" ]; do
508             local dirPath=$(dirname "$filePath")
509             cp -f "$filePath" "$coreRootDir"
510         done
511 }
512
513 # Variables for unsupported and failing tests
514 declare -a unsupportedTests
515 declare -a failingTests
516 declare -a playlistTests
517 ((runFailingTestsOnly = 0))
518
519 # Get an array of items by reading the specified file line by line.
520 function read_array {
521     local theArray=()
522
523     if [ ! -f "$1" ]; then
524         return
525     fi
526
527     # bash in Mac OS X doesn't support 'readarray', so using alternate way instead.
528     # readarray -t theArray < "$1"
529     # Any line that starts with '#' is ignored.
530     while IFS='' read -r line || [ -n "$line" ]; do
531         if [[ $line != "#"* ]]; then
532             theArray[${#theArray[@]}]=$line
533         fi
534     done < "$1"
535     echo ${theArray[@]}
536 }
537
538 function load_unsupported_tests {
539     # Load the list of tests that are not supported on this platform. These tests are disabled (skipped) permanently.
540     unsupportedTests=($(read_array "$(dirname "$0")/testsUnsupportedOutsideWindows.txt"))
541     unsupportedTests+=($(read_array "$(dirname "$0")/testsUnsupported.$ARCH.txt"))
542 }
543
544 function load_failing_tests {
545     # Load the list of tests that fail on this platform. These tests are disabled (skipped) temporarily, pending investigation.
546     failingTests=($(read_array "$(dirname "$0")/testsFailingOutsideWindows.txt"))
547     failingTests+=($(read_array "$(dirname "$0")/testsFailing.$ARCH.txt"))
548 }
549
550 function load_playlist_tests {
551     # Load the list of tests that are enabled as a part of this test playlist.
552     playlistTests=($(read_array "${playlistFile}"))
553 }
554
555 function is_unsupported_test {
556     for unsupportedTest in "${unsupportedTests[@]}"; do
557         if [ "$1" == "$unsupportedTest" ]; then
558             return 0
559         fi
560     done
561     return 1
562 }
563
564 function is_failing_test {
565     for failingTest in "${failingTests[@]}"; do
566         if [ "$1" == "$failingTest" ]; then
567             return 0
568         fi
569     done
570     return 1
571 }
572
573 function is_playlist_test {
574     for playlistTest in "${playlistTests[@]}"; do
575         if [ "$1" == "$playlistTest" ]; then
576             return 0
577         fi
578     done
579     return 1
580 }
581
582 function skip_unsupported_test {
583     # This function runs in a background process. It should not echo anything, and should not use global variables. This
584     # function is analogous to run_test, and causes the test to be skipped with the message below.
585
586     local scriptFilePath=$1
587     local outputFilePath=$2
588
589     echo "Not supported on this platform." >"$outputFilePath"
590     return 2 # skip the test
591 }
592
593 function skip_failing_test {
594     # This function runs in a background process. It should not echo anything, and should not use global variables. This
595     # function is analogous to run_test, and causes the test to be skipped with the message below.
596
597     local scriptFilePath=$1
598     local outputFilePath=$2
599
600     echo "Temporarily disabled on this platform due to unexpected failures." >"$outputFilePath"
601     return 2 # skip the test
602 }
603
604 function skip_non_playlist_test {
605     # This function runs in a background process. It should not echo anything, and should not use global variables. This
606     # function is analogous to run_test, and causes the test to be skipped with the message below.
607
608     local scriptFilePath=$1
609     local outputFilePath=$2
610
611     echo "Test is not included in the running playlist." >"$outputFilePath"
612     return 2 # skip the test
613 }
614
615 function set_up_core_dump_generation {
616     # We will only enable dump generation here if we're on Mac or Linux
617     if [[ ! ( "$(uname -s)" == "Darwin" || "$(uname -s)" == "Linux" ) ]]; then
618         return
619     fi
620
621     # We won't enable dump generation on OS X/macOS if the machine hasn't been
622     # configured with the kern.corefile pattern we expect.
623     if [[ ( "$(uname -s)" == "Darwin" && "$(sysctl -n kern.corefile)" != "core.%P" ) ]]; then
624         echo "WARNING: Core dump generation not being enabled due to unexpected kern.corefile value."
625         return
626     fi
627
628     # Allow dump generation
629     ulimit -c unlimited
630
631     if [ "$(uname -s)" == "Linux" ]; then
632         if [ -e /proc/self/coredump_filter ]; then
633             # Include memory in private and shared file-backed mappings in the dump.
634             # This ensures that we can see disassembly from our shared libraries when
635             # inspecting the contents of the dump. See 'man core' for details.
636             echo 0x3F > /proc/self/coredump_filter
637         fi
638     fi
639 }
640
641 function print_info_from_core_file {
642
643     #### temporary
644     if [ "$ARCH" == "arm64" ]; then
645         echo "Not inspecting core dumps on arm64 at the moment."
646         return
647     fi
648     ####
649
650     local core_file_name=$1
651     local executable_name=$2
652
653     if ! [ -e $executable_name ]; then
654         echo "Unable to find executable $executable_name"
655         return
656     elif ! [ -e $core_file_name ]; then
657         echo "Unable to find core file $core_file_name"
658         return
659     fi
660
661     # Use LLDB to inspect the core dump on Mac, and GDB everywhere else.
662     if [[ "$OSName" == "Darwin" ]]; then
663         hash lldb 2>/dev/null || { echo >&2 "LLDB was not found. Unable to print core file."; return; }
664
665         echo "Printing info from core file $core_file_name"
666         lldb -c $core_file_name -b -o 'bt'
667     else
668         # Use GDB to print the backtrace from the core file.
669         hash gdb 2>/dev/null || { echo >&2 "GDB was not found. Unable to print core file."; return; }
670
671         echo "Printing info from core file $core_file_name"
672         gdb --batch -ex "thread apply all bt full" -ex "quit" $executable_name $core_file_name
673     fi
674 }
675
676 function download_dumpling_script {
677     echo "Downloading latest version of dumpling script."
678     wget "https://dumpling.azurewebsites.net/api/client/dumpling.py"
679
680     local dumpling_script="dumpling.py"
681     chmod +x $dumpling_script
682 }
683
684 function upload_core_file_to_dumpling {
685     local core_file_name=$1
686     local dumpling_script="dumpling.py"
687     local dumpling_file="local_dumplings.txt"
688
689     # dumpling requires that the file exist before appending.
690     touch ./$dumpling_file
691
692     if [ ! -x $dumpling_script ]; then
693         download_dumpling_script
694     fi
695
696     if [ ! -x $dumpling_script ]; then
697         echo "Failed to download dumpling script. Dump cannot be uploaded."
698         return
699     fi
700
701     echo "Uploading $core_file_name to dumpling service."
702
703     local paths_to_add=""
704     if [ -d "$coreClrBinDir" ]; then
705         echo "Uploading CoreCLR binaries with dump."
706         paths_to_add=$coreClrBinDir
707     fi
708
709     # Ensure the script has Unix line endings
710     perl -pi -e 's/\r\n|\n|\r/\n/g' "$dumpling_script"
711
712     # The output from this will include a unique ID for this dump.
713     ./$dumpling_script "upload" "--dumppath" "$core_file_name" "--incpaths" $paths_to_add "--properties" "Project=CoreCLR" "--squelch" | tee -a $dumpling_file
714 }
715
716 function preserve_core_file {
717     local core_file_name=$1
718     local storage_location="/tmp/coredumps_coreclr"
719
720     # Create the directory (this shouldn't fail even if it already exists).
721     mkdir -p $storage_location
722
723     # Only preserve the dump if the directory is empty. Otherwise, do nothing.
724     # This is a way to prevent us from storing/uploading too many dumps.
725     if [ ! "$(ls -A $storage_location)" ]; then
726         echo "Copying core file $core_file_name to $storage_location"
727         cp $core_file_name $storage_location
728
729         upload_core_file_to_dumpling $core_file_name
730     fi
731 }
732
733 function inspect_and_delete_core_files {
734     # This function prints some basic information from core files in the current
735     # directory and deletes them immediately. Based on the state of the system, it may
736     # also upload a core file to the dumpling service.
737     # (see preserve_core_file).
738     
739     # Depending on distro/configuration, the core files may either be named "core"
740     # or "core.<PID>" by default. We will read /proc/sys/kernel/core_uses_pid to 
741     # determine which one it is.
742     # On OS X/macOS, we checked the kern.corefile value before enabling core dump
743     # generation, so we know it always includes the PID.
744     local core_name_uses_pid=0
745     if [[ (( -e /proc/sys/kernel/core_uses_pid ) && ( "1" == $(cat /proc/sys/kernel/core_uses_pid) )) 
746           || ( "$(uname -s)" == "Darwin" ) ]]; then
747         core_name_uses_pid=1
748     fi
749
750     if [ $core_name_uses_pid == "1" ]; then
751         # We don't know what the PID of the process was, so let's look at all core
752         # files whose name matches core.NUMBER
753         for f in core.*; do
754             [[ $f =~ core.[0-9]+ ]] && print_info_from_core_file "$f" $CORE_ROOT/"corerun" && preserve_core_file "$f" && rm "$f"
755         done
756     elif [ -f core ]; then
757         print_info_from_core_file "core" $CORE_ROOT/"corerun"
758         preserve_core_file "core"
759         rm "core"
760     fi
761 }
762
763 function run_test {
764     # This function runs in a background process. It should not echo anything, and should not use global variables.
765
766     local scriptFilePath=$1
767     local outputFilePath=$2
768
769     # Switch to directory where the script is
770     cd "$(dirname "$scriptFilePath")"
771
772     local scriptFileName=$(basename "$scriptFilePath")
773     local outputFileName=$(basename "$outputFilePath")
774
775     if [ "$limitedCoreDumps" == "ON" ]; then
776         set_up_core_dump_generation
777     fi
778
779     "./$scriptFileName" >"$outputFileName" 2>&1
780     local testScriptExitCode=$?
781
782     # We will try to print some information from generated core dumps if a debugger
783     # is available, and possibly store a dump in a non-transient location.
784     if [ "$limitedCoreDumps" == "ON" ]; then
785         inspect_and_delete_core_files
786     fi
787
788     return $testScriptExitCode
789 }
790
791 # Variables for running tests in the background
792 if [ `uname` = "NetBSD" ]; then
793     NumProc=$(getconf NPROCESSORS_ONLN)
794 elif [ `uname` = "Darwin" ]; then
795     NumProc=$(getconf _NPROCESSORS_ONLN)
796 else
797     if [ -x "$(command -v nproc)" ]; then
798         NumProc=$(nproc --all)
799     elif [ -x "$(command -v getconf)" ]; then
800         NumProc=$(getconf _NPROCESSORS_ONLN)
801     else
802         NumProc=1
803     fi
804 fi
805 ((maxProcesses = $NumProc * 3 / 2)) # long tests delay process creation, use a few more processors
806
807 ((processCount = 0))
808 declare -a scriptFilePaths
809 declare -a outputFilePaths
810 declare -a processIds
811 declare -a testStartTimes
812 waitProcessIndex=
813 pidNone=0
814
815 function waitany {
816     local pid
817     local exitcode
818     while true; do
819         for (( i=0; i<$maxProcesses; i++ )); do
820             pid=${processIds[$i]}
821             if [ -z "$pid" ] || [ "$pid" == "$pidNone" ]; then
822                 continue
823             fi
824             if ! kill -0 $pid 2>/dev/null; then
825                 wait $pid
826                 exitcode=$?
827                 waitProcessIndex=$i
828                 processIds[$i]=$pidNone
829                 return $exitcode
830             fi
831         done
832         sleep 0.1
833     done
834 }
835
836 function get_available_process_index {
837     local pid
838     local i=0
839     for (( i=0; i<$maxProcesses; i++ )); do
840         pid=${processIds[$i]}
841         if [ -z "$pid" ] || [ "$pid" == "$pidNone" ]; then
842             break
843         fi
844     done
845     echo $i
846 }
847
848 function finish_test {
849     waitany
850     local testScriptExitCode=$?
851     local finishedProcessIndex=$waitProcessIndex
852     ((--processCount))
853
854     local scriptFilePath=${scriptFilePaths[$finishedProcessIndex]}
855     local outputFilePath=${outputFilePaths[$finishedProcessIndex]}
856     local scriptFileName=$(basename "$scriptFilePath")
857
858     local testEndTime=
859     local testRunningTime=
860     local header=
861
862     if ((verbose == 1)); then
863         header=$(printf "[%4d]" $countTotalTests)
864     fi
865
866     if [ "$showTime" == "ON" ]; then
867         testEndTime=$(date +%s)
868         testRunningTime=$(( $testEndTime - ${testStartTimes[$finishedProcessIndex]} ))
869         header=$header$(printf "[%4ds]" $testRunningTime)
870     fi
871
872     local testResult
873     case $testScriptExitCode in
874         0)
875             let countPassedTests++
876             testResult='Pass'
877             if ((verbose == 1 || runFailingTestsOnly == 1)); then
878                 echo "PASSED   - ${header}${scriptFilePath}"
879             else
880                 echo "         - ${header}${scriptFilePath}"
881             fi
882             ;;
883         2)
884             let countSkippedTests++
885             testResult='Skip'
886             echo "SKIPPED  - ${header}${scriptFilePath}"
887             ;;
888         *)
889             let countFailedTests++
890             testResult='Fail'
891             echo "FAILED   - ${header}${scriptFilePath}"
892             ;;
893     esac
894     let countTotalTests++
895
896     if ((verbose == 1 || testScriptExitCode != 0)); then
897         while IFS='' read -r line || [ -n "$line" ]; do
898             echo "               $line"
899         done <"$outputFilePath"
900     fi
901
902     xunit_output_add_test "$scriptFilePath" "$outputFilePath" "$testResult" "$testScriptExitCode" "$testRunningTime"
903     text_file_output_add_test "$scriptFilePath" "$testResult"
904 }
905
906 function finish_remaining_tests {
907     # Finish the remaining tests in the order in which they were started
908     while ((processCount > 0)); do
909         finish_test
910     done
911 }
912
913 function prep_test {
914     local scriptFilePath=$1
915     local scriptFileDir=$(dirname "$scriptFilePath")
916
917     test "$verbose" == 1 && echo "Preparing $scriptFilePath"
918
919     if [ ! "$noLFConversion" == "ON" ]; then
920         # Convert DOS line endings to Unix if needed
921         perl -pi -e 's/\r\n|\n|\r/\n/g' "$scriptFilePath"
922     fi
923         
924     # Add executable file mode bit if needed
925     chmod +x "$scriptFilePath"
926
927     #remove any NI and Locks
928     rm -f $scriptFileDir/*.ni.*
929     rm -rf $scriptFileDir/lock
930 }
931
932 function start_test {
933     local nextProcessIndex=$(get_available_process_index)
934     local scriptFilePath=$1
935     if ((runFailingTestsOnly == 1)) && ! is_failing_test "$scriptFilePath"; then
936         return
937     fi
938
939     # Skip any test that's not in the current playlist, if a playlist was
940     # given to us.
941     if [ -n "$playlistFile" ] && ! is_playlist_test "$scriptFilePath"; then
942         return
943     fi
944
945     if ((nextProcessIndex == maxProcesses)); then
946         finish_test
947         nextProcessIndex=$(get_available_process_index)
948     fi
949
950     scriptFilePaths[$nextProcessIndex]=$scriptFilePath
951     local scriptFileName=$(basename "$scriptFilePath")
952     local outputFilePath=$(dirname "$scriptFilePath")/${scriptFileName}.out
953     outputFilePaths[$nextProcessIndex]=$outputFilePath
954
955     if [ "$showTime" == "ON" ]; then
956         testStartTimes[$nextProcessIndex]=$(date +%s)
957     fi
958
959     test "$verbose" == 1 && echo "Starting $scriptFilePath"
960     if is_unsupported_test "$scriptFilePath"; then
961         skip_unsupported_test "$scriptFilePath" "$outputFilePath" &
962     elif ((runFailingTestsOnly == 0)) && is_failing_test "$scriptFilePath"; then
963         skip_failing_test "$scriptFilePath" "$outputFilePath" &
964     else
965         run_test "$scriptFilePath" "$outputFilePath" &
966     fi
967     processIds[$nextProcessIndex]=$!
968
969     ((++processCount))
970 }
971
972 # Get a list of directories in which to scan for tests by reading the
973 # specified file line by line.
974 function set_test_directories {
975     local errorSource='set_test_directories'
976
977     local listFileName=$1
978
979     if [ ! -f "$listFileName" ]
980     then
981         exit_with_error "$errorSource" "Test directories file not found at $listFileName"
982     fi
983     testDirectories=($(read_array "$listFileName"))
984 }
985
986 function run_tests_in_directory {
987     local testDir=$1
988
989     # Recursively search through directories for .sh files to prepare them.
990     # Note: This needs to occur before any test runs as some of the .sh files
991     # depend on other .sh files
992     for scriptFilePath in $(find "$testDir" -type f -iname '*.sh' | sort)
993     do
994         prep_test "${scriptFilePath:2}"
995     done
996     echo "The tests have been prepared"
997     # Recursively search through directories for .sh files to run.
998     for scriptFilePath in $(find "$testDir" -type f -iname '*.sh' | sort)
999     do
1000         start_test "${scriptFilePath:2}"
1001     done
1002 }
1003
1004 function coreclr_code_coverage {
1005     local coverageDir="$coverageOutputDir/Coverage"
1006     local toolsDir="$coverageOutputDir/Coverage/tools"
1007     local reportsDir="$coverageOutputDir/Coverage/reports"
1008     local packageName="unix-code-coverage-tools.1.0.0.nupkg"
1009
1010     rm -rf $coverageDir
1011     mkdir -p $coverageDir
1012     mkdir -p $toolsDir
1013     mkdir -p $reportsDir
1014     pushd $toolsDir > /dev/null
1015
1016     echo "Pulling down code coverage tools"
1017     wget -q https://www.myget.org/F/dotnet-buildtools/api/v2/package/unix-code-coverage-tools/1.0.0 -O $packageName
1018     echo "Unzipping to $toolsDir"
1019     unzip -q -o $packageName
1020
1021     # Invoke gcovr
1022     chmod a+rwx ./gcovr
1023     chmod a+rwx ./$OSName/llvm-cov
1024
1025     echo
1026     echo "Generating coreclr code coverage reports at $reportsDir/coreclr.html"
1027     echo "./gcovr $coreClrObjs --gcov-executable=$toolsDir/$OS/llvm-cov -r $coreClrSrc --html --html-details -o $reportsDir/coreclr.html"
1028     echo
1029     ./gcovr $coreClrObjs --gcov-executable=$toolsDir/$OSName/llvm-cov -r $coreClrSrc --html --html-details -o $reportsDir/coreclr.html
1030     exitCode=$?
1031     popd > /dev/null
1032     exit $exitCode
1033 }
1034
1035 function check_cpu_architecture {
1036     local CPUName=$(uname -m)
1037     local __arch=
1038
1039     case $CPUName in
1040         i686)
1041             __arch=x86
1042             ;;
1043         x86_64)
1044             __arch=x64
1045             ;;
1046         armv7l)
1047             __arch=arm
1048             ;;
1049         aarch64)
1050             __arch=arm64
1051             ;;
1052         *)
1053             echo "Unknown CPU $CPUName detected, configuring as if for x64"
1054             __arch=x64
1055             ;;
1056     esac
1057
1058     echo "$__arch"
1059 }
1060
1061 ARCH=$(check_cpu_architecture)
1062 echo "Running on  CPU- $ARCH"
1063
1064 # Exit code constants
1065 readonly EXIT_CODE_SUCCESS=0       # Script ran normally.
1066 readonly EXIT_CODE_EXCEPTION=1     # Script exited because something exceptional happened (e.g. bad arguments, Ctrl-C interrupt).
1067 readonly EXIT_CODE_TEST_FAILURE=2  # Script completed successfully, but one or more tests failed.
1068
1069 # Argument variables
1070 testRootDir=
1071 testNativeBinDir=
1072 coreOverlayDir=
1073 coreClrBinDir=
1074 mscorlibDir=
1075 coreFxBinDir=
1076 coreClrObjs=
1077 coreClrSrc=
1078 coverageOutputDir=
1079 testEnv=
1080 playlistFile=
1081 showTime=
1082 noLFConversion=
1083 buildOverlayOnly=
1084 gcsimulator=
1085 longgc=
1086 limitedCoreDumps=
1087 illinker=
1088 ((disableEventLogging = 0))
1089 ((serverGC = 0))
1090
1091 # Handle arguments
1092 verbose=0
1093 doCrossgen=0
1094 jitdisasm=0
1095 ilasmroundtrip=
1096
1097 for i in "$@"
1098 do
1099     case $i in
1100         -h|--help)
1101             print_usage
1102             exit $EXIT_CODE_SUCCESS
1103             ;;
1104         -v|--verbose)
1105             verbose=1
1106             ;;
1107         --crossgen)
1108             doCrossgen=1
1109             ;;
1110         --jitstress=*)
1111             export COMPlus_JitStress=${i#*=}
1112             ;;
1113         --jitstressregs=*)
1114             export COMPlus_JitStressRegs=${i#*=}
1115             ;;
1116         --jitminopts)
1117             export COMPlus_JITMinOpts=1
1118             ;;
1119         --copyNativeTestBin)
1120             export copyNativeTestBin=1
1121             ;;
1122         --jitforcerelocs)
1123             export COMPlus_ForceRelocs=1
1124             ;;
1125         --link=*)
1126             export ILLINK=${i#*=}
1127             export DoLink=true
1128             ;;
1129         --tieredcompilation)
1130             export COMPlus_EXPERIMENTAL_TieredCompilation=1
1131             ;;
1132         --jitdisasm)
1133             jitdisasm=1
1134             ;;
1135         --ilasmroundtrip)
1136             ((ilasmroundtrip = 1))
1137             ;;
1138         --testRootDir=*)
1139             testRootDir=${i#*=}
1140             ;;
1141         --testNativeBinDir=*)
1142             testNativeBinDir=${i#*=}
1143             ;;
1144         --coreOverlayDir=*)
1145             coreOverlayDir=${i#*=}
1146             ;;
1147         --coreClrBinDir=*)
1148             coreClrBinDir=${i#*=}
1149             ;;
1150         --mscorlibDir=*)
1151             mscorlibDir=${i#*=}
1152             ;;
1153         --coreFxBinDir=*)
1154             coreFxBinDir=${i#*=}
1155             ;;
1156         --testDir=*)
1157             testDirectories[${#testDirectories[@]}]=${i#*=}
1158             ;;
1159         --testDirFile=*)
1160             set_test_directories "${i#*=}"
1161             ;;
1162         --runFailingTestsOnly)
1163             ((runFailingTestsOnly = 1))
1164             ;;
1165         --disableEventLogging)
1166             ((disableEventLogging = 1))
1167             ;;
1168         --runcrossgentests)
1169             export RunCrossGen=1
1170             ;;
1171         --sequential)
1172             ((maxProcesses = 1))
1173             ;;
1174         --useServerGC)
1175             ((serverGC = 1))
1176             ;;
1177         --long-gc)
1178             ((longgc = 1))
1179             ;;
1180         --gcsimulator)
1181             ((gcsimulator = 1))
1182             ;;
1183         --playlist=*)
1184             playlistFile=${i#*=}
1185             ;;
1186         --coreclr-coverage)
1187             CoreClrCoverage=ON
1188             ;;
1189         --coreclr-objs=*)
1190             coreClrObjs=${i#*=}
1191             ;;
1192         --coreclr-src=*)
1193             coreClrSrc=${i#*=}
1194             ;;
1195         --coverage-output-dir=*)
1196             coverageOutputDir=${i#*=}
1197             ;;
1198         --test-env=*)
1199             testEnv=${i#*=}
1200             ;;            
1201         --gcstresslevel=*)
1202             export COMPlus_GCStress=${i#*=}
1203             ;;            
1204         --gcname=*)
1205             export COMPlus_GCName=${i#*=}
1206             ;;
1207         --show-time)
1208             showTime=ON
1209             ;;
1210         --no-lf-conversion)
1211             noLFConversion=ON
1212             ;;
1213         --build-overlay-only)
1214             buildOverlayOnly=ON
1215             ;;
1216         --limitedDumpGeneration)
1217             limitedCoreDumps=ON
1218             ;;
1219         --xunitOutputPath=*)
1220             xunitOutputPath=${i#*=}
1221             ;;
1222         *)
1223             echo "Unknown switch: $i"
1224             print_usage
1225             exit $EXIT_CODE_SUCCESS
1226             ;;
1227     esac
1228 done
1229
1230 if [ -n "$coreOverlayDir" ] && [ "$buildOverlayOnly" == "ON" ]; then
1231     echo "Can not use \'--coreOverlayDir=<path>\' and \'--build-overlay-only\' at the same time."
1232     exit $EXIT_CODE_EXCEPTION
1233 fi
1234
1235 if ((disableEventLogging == 0)); then
1236     export COMPlus_EnableEventLog=1
1237 fi
1238
1239 export CORECLR_SERVER_GC="$serverGC"
1240
1241 if [ -z "$testRootDir" ]; then
1242     echo "--testRootDir is required."
1243     print_usage
1244     exit $EXIT_CODE_EXCEPTION
1245 fi
1246 if [ ! -d "$testRootDir" ]; then
1247     echo "Directory specified by --testRootDir does not exist: $testRootDir"
1248     exit $EXIT_CODE_EXCEPTION
1249 fi
1250
1251 # Copy native interop test libraries over to the mscorlib path in
1252 # order for interop tests to run on linux.
1253 if [ -z "$mscorlibDir" ]; then
1254     mscorlibDir=$coreClrBinDir
1255 fi
1256
1257 if [ ! -z "$longgc" ]; then
1258     echo "Running Long GC tests"
1259     export RunningLongGCTests=1
1260 fi
1261
1262 if [ ! -z "$gcsimulator" ]; then
1263     echo "Running GC simulator tests"
1264     export RunningGCSimulatorTests=1
1265 fi
1266
1267 if [[ ! "$jitdisasm" -eq 0 ]]; then
1268     echo "Running jit disasm"
1269     export RunningJitDisasm=1
1270 fi
1271
1272 if [ ! -z "$ilasmroundtrip" ]; then
1273     echo "Running Ilasm round trip"
1274     export RunningIlasmRoundTrip=1
1275 fi
1276
1277 # If this is a coverage run, make sure the appropriate args have been passed
1278 if [ "$CoreClrCoverage" == "ON" ]
1279 then
1280     echo "Code coverage is enabled for this run"
1281     echo ""
1282     if [ ! "$OSName" == "Darwin" ] && [ ! "$OSName" == "Linux" ]
1283     then
1284         echo "Code Coverage not supported on $OS"
1285         exit 1
1286     fi
1287
1288     if [ -z "$coreClrObjs" ]
1289     then
1290         echo "Coreclr obj files are required to generate code coverage reports"
1291         echo "Coreclr obj files root path can be passed using '--coreclr-obj' argument"
1292         exit 1
1293     fi
1294
1295     if [ -z "$coreClrSrc" ]
1296     then
1297         echo "Coreclr src files are required to generate code coverage reports"
1298         echo "Coreclr src files root path can be passed using '--coreclr-src' argument"
1299         exit 1
1300     fi
1301
1302     if [ -z "$coverageOutputDir" ]
1303     then
1304         echo "Output directory for coverage results must be specified"
1305         echo "Output path can be specified '--coverage-output-dir' argument"
1306         exit 1
1307     fi
1308 fi
1309
1310 xunit_output_begin
1311 text_file_output_begin
1312 create_core_overlay
1313 precompile_overlay_assemblies
1314
1315 if [ "$buildOverlayOnly" == "ON" ];
1316 then
1317     echo "Build overlay directory '$coreOverlayDir' complete."
1318     exit 0
1319 fi
1320
1321 if [ -n "$playlistFile" ]
1322 then
1323     # Use a playlist file exclusively, if it was provided
1324     echo "Executing playlist $playlistFile"
1325     load_playlist_tests
1326 else
1327     load_unsupported_tests
1328     load_failing_tests
1329 fi
1330
1331 # Other architectures are not supported yet.
1332 if [ "$ARCH" == "x64" ]
1333 then
1334     scriptPath=$(dirname $0)
1335     ${scriptPath}/setup-stress-dependencies.sh --outputDir=$coreOverlayDir
1336 else
1337     if [ "$ARCH" != "arm64" ]
1338     then
1339         echo "Skip preparing for GC stress test. Dependent package is not supported on this architecture."
1340     fi
1341 fi
1342
1343 export __TestEnv=$testEnv
1344
1345 cd "$testRootDir"
1346
1347 dumplingsListPath="$testRootDir/dumplings.txt"
1348
1349 # clean up any existing dumpling remnants from previous runs.
1350 rm -f "$dumplingsListPath"
1351 find $testRootDir -type f -name "local_dumplings.txt" -exec rm {} \;
1352
1353 time_start=$(date +"%s")
1354 if [ -z "$testDirectories" ]
1355 then
1356     # No test directories were specified, so run everything in the current
1357     # directory and its subdirectories.
1358     run_tests_in_directory "."
1359 else
1360     # Otherwise, run all the tests in each specified test directory.
1361     for testDir in "${testDirectories[@]}"
1362     do
1363         if [ ! -d "$testDir" ]; then
1364             echo "Test directory does not exist: $testDir"
1365         else
1366             run_tests_in_directory "./$testDir"
1367         fi
1368     done
1369 fi
1370 finish_remaining_tests
1371
1372 print_results
1373
1374 find $testRootDir -type f -name "local_dumplings.txt" -exec cat {} \; > $dumplingsListPath
1375
1376 if [ -s $dumplingsListPath ]; then
1377     cat $dumplingsListPath
1378 else
1379     rm $dumplingsListPath
1380 fi
1381
1382 time_end=$(date +"%s")
1383 time_diff=$(($time_end-$time_start))
1384 echo "$(($time_diff / 60)) minutes and $(($time_diff % 60)) seconds taken to run CoreCLR tests."
1385
1386 xunit_output_end
1387
1388 if [ "$CoreClrCoverage" == "ON" ]
1389 then
1390     coreclr_code_coverage
1391 fi
1392
1393 if ((countFailedTests > 0)); then
1394     exit $EXIT_CODE_TEST_FAILURE
1395 fi
1396
1397 exit $EXIT_CODE_SUCCESS