5 echo 'CoreCLR test runner script.'
7 echo 'Typical command line:'
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 ' --coreClrBinDir="coreclr/bin/Product/Linux.x64.Debug"'
13 echo ' --mscorlibDir="windows/coreclr/bin/Product/Linux.x64.Debug"'
14 echo ' --coreFxBinDir="corefx/bin/runtime/netcoreapp-Linux-Debug-x64'
16 echo 'Required arguments:'
17 echo ' --testRootDir=<path> : Root directory of the test build (e.g. coreclr/bin/tests/Windows_NT.x64.Debug).'
18 echo ' --testNativeBinDir=<path> : Directory of the native CoreCLR test build (e.g. coreclr/bin/obj/Linux.x64.Debug/tests).'
19 echo ' (Also required: Either --coreOverlayDir, or all of the switches --coreOverlayDir overrides)'
21 echo 'Optional arguments:'
22 echo ' --coreOverlayDir=<path> : Directory containing core binaries and test dependencies. If not specified, the'
23 echo ' default is testRootDir/Tests/coreoverlay. This switch overrides --coreClrBinDir,'
24 echo ' --mscorlibDir, and --coreFxBinDir.'
25 echo ' --coreClrBinDir=<path> : Directory of the CoreCLR build (e.g. coreclr/bin/Product/Linux.x64.Debug).'
26 echo ' --mscorlibDir=<path> : Directory containing the built mscorlib.dll. If not specified, it is expected to be'
27 echo ' in the directory specified by --coreClrBinDir.'
28 echo ' --coreFxBinDir="<path>" : Directory with CoreFX build outputs'
29 echo ' (e.g. "corefx/bin/runtime/netcoreapp-Linux-Debug-x64")'
30 echo ' If files with the same name are present in multiple directories, the first one wins.'
31 echo ' --testDir=<path> : Run tests only in the specified directory. The path is relative to the directory'
32 echo ' specified by --testRootDir. Multiple of this switch may be specified.'
33 echo ' --testDirFile=<path> : Run tests only in the directories specified by the file at <path>. Paths are listed'
34 echo ' one line, relative to the directory specified by --testRootDir.'
35 echo ' --build-overlay-only : Build coreoverlay only, and skip running tests.'
36 echo ' --runFailingTestsOnly : Run only the tests that are disabled on this platform due to unexpected failures.'
37 echo ' Failing tests are listed in coreclr/tests/failingTestsOutsideWindows.txt, one per'
38 echo ' line, as paths to .sh files relative to the directory specified by --testRootDir.'
39 echo ' --disableEventLogging : Disable the events logged by both VM and Managed Code'
40 echo ' --sequential : Run tests sequentially (default is to run in parallel).'
41 echo ' --playlist=<path> : Run only the tests that are specified in the file at <path>, in the same format as'
42 echo ' runFailingTestsOnly'
43 echo ' -v, --verbose : Show output from each test.'
44 echo ' -h|--help : Show usage information.'
45 echo ' --useServerGC : Enable server GC for this test run'
46 echo ' --test-env : Script to set environment variables for tests'
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)'
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'
80 function print_results {
82 echo "======================="
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 "======================="
93 # Initialize counters for bookkeeping.
99 # Variables for xUnit-style XML output. XML format: https://xunit.github.io/docs/format-xml-v2.html
103 # libExtension determines extension for dynamic library files
104 # runtimeName determines where CoreFX Runtime files will be located
121 echo "Unsupported OS $OSName detected, configuring as if for Linux"
126 function xunit_output_begin {
127 if [ -z "$xunitOutputPath" ]; then
128 xunitOutputPath=$testRootDir/coreclrtests.xml
130 if ! [ -e $(basename "$xunitOutputPath") ]; then
131 xunitOutputPath=$testRootDir/coreclrtests.xml
133 xunitTestOutputPath=${xunitOutputPath}.test
134 if [ -e "$xunitOutputPath" ]; then
135 rm -f -r "$xunitOutputPath"
137 if [ -e "$xunitTestOutputPath" ]; then
138 rm -f -r "$xunitTestOutputPath"
142 function xunit_output_add_test {
146 # <test .../> <!-- Write this element here -->
148 local scriptFilePath=$1
149 local outputFilePath=$2
150 local testResult=$3 # Pass, Fail, or Skip
151 local testScriptExitCode=$4
152 local testRunningTime=$5
154 local testPath=${scriptFilePath%.sh} # Remove trailing ".sh"
155 local testDir=$(dirname "$testPath")
156 local testName=$(basename "$testPath")
158 # Replace '/' with '.'
159 testPath=$(echo "$testPath" | tr / .)
160 testDir=$(echo "$testDir" | tr / .)
166 line="${line} name=\"${testPath}\""
167 line="${line} type=\"${testDir}\""
168 line="${line} method=\"${testName}\""
169 line="${line} result=\"${testResult}\""
170 if [ -n "$testRunningTime" ] && [ "$testResult" != "Skip" ]; then
171 line="${line} time=\"${testRunningTime}\""
174 if [ "$testResult" == "Pass" ]; then
176 echo "$line" >>"$xunitTestOutputPath"
181 echo "$line" >>"$xunitTestOutputPath"
184 if [ "$testResult" == "Skip" ]; then
185 line="${line}<reason><![CDATA[$(cat "$outputFilePath")]]></reason>"
186 echo "$line" >>"$xunitTestOutputPath"
188 line="${line}<failure exception-type=\"Exit code: ${testScriptExitCode}\">"
189 echo "$line" >>"$xunitTestOutputPath"
192 line="${line}<message>"
193 echo "$line" >>"$xunitTestOutputPath"
195 line="${line}<![CDATA["
196 echo "$line" >>"$xunitTestOutputPath"
197 cat "$outputFilePath" >>"$xunitTestOutputPath"
200 echo "$line" >>"$xunitTestOutputPath"
202 line="${line}</message>"
203 echo "$line" >>"$xunitTestOutputPath"
206 line="${line}</failure>"
207 echo "$line" >>"$xunitTestOutputPath"
211 line="${line}</test>"
212 echo "$line" >>"$xunitTestOutputPath"
215 function xunit_output_end {
217 local errorMessage=$2
220 if [ -z "$errorSource" ]; then
226 echo '<?xml version="1.0" encoding="utf-8"?>' >>"$xunitOutputPath"
227 echo '<assemblies>' >>"$xunitOutputPath"
233 line="${line}<assembly"
234 line="${line} name=\"CoreClrTestAssembly\""
235 line="${line} total=\"${countTotalTests}\""
236 line="${line} passed=\"${countPassedTests}\""
237 line="${line} failed=\"${countFailedTests}\""
238 line="${line} skipped=\"${countSkippedTests}\""
239 line="${line} errors=\"${errorCount}\""
241 echo "$line" >>"$xunitOutputPath"
245 line="${line}<collection"
246 line="${line} name=\"CoreClrTestCollection\""
247 line="${line} total=\"${countTotalTests}\""
248 line="${line} passed=\"${countPassedTests}\""
249 line="${line} failed=\"${countFailedTests}\""
250 line="${line} skipped=\"${countSkippedTests}\""
252 echo "$line" >>"$xunitOutputPath"
254 # <test .../> <test .../> ...
255 if [ -f "$xunitTestOutputPath" ]; then
256 cat "$xunitTestOutputPath" >>"$xunitOutputPath"
257 rm -f "$xunitTestOutputPath"
262 line="${line}</collection>"
263 echo "$line" >>"$xunitOutputPath"
265 if [ -n "$errorSource" ]; then
268 line="${line}<errors>"
269 echo "$line" >>"$xunitOutputPath"
274 line="${line} type=\"TestHarnessError\""
275 line="${line} name=\"${errorSource}\""
277 echo "$line" >>"$xunitOutputPath"
281 line="${line}<failure>${errorMessage}</failure>"
282 echo "$line" >>"$xunitOutputPath"
286 line="${line}</error>"
287 echo "$line" >>"$xunitOutputPath"
291 line="${line}</errors>"
292 echo "$line" >>"$xunitOutputPath"
297 line="${line}</assembly>"
298 echo "$line" >>"$xunitOutputPath"
301 echo '</assemblies>' >>"$xunitOutputPath"
304 function exit_with_error {
306 local errorMessage=$2
309 if [ -z "$printUsage" ]; then
314 xunit_output_end "$errorSource" "$errorMessage"
315 if ((printUsage != 0)); then
318 exit $EXIT_CODE_EXCEPTION
321 # Handle Ctrl-C. We will stop execution and print the results that
322 # we gathered so far.
323 function handle_ctrl_c {
324 local errorSource='handle_ctrl_c'
327 echo "*** Stopping... ***"
329 exit_with_error "$errorSource" "Test run aborted by Ctrl+C."
332 # Register the Ctrl-C handler
333 trap handle_ctrl_c INT
335 function create_core_overlay {
336 local errorSource='create_core_overlay'
339 if [ -n "$coreOverlayDir" ]; then
340 export CORE_ROOT="$coreOverlayDir"
344 # Check inputs to make sure we have enough information to create the core layout. $testRootDir/Tests/Core_Root should
345 # already exist and contain test dependencies that are not built.
346 local testDependenciesDir=$testRootDir/Tests/Core_Root
347 if [ ! -d "$testDependenciesDir" ]; then
348 exit_with_error "$errorSource" "Did not find the test dependencies directory: $testDependenciesDir"
350 if [ -z "$coreClrBinDir" ]; then
351 exit_with_error "$errorSource" "One of --coreOverlayDir or --coreClrBinDir must be specified." "$printUsage"
353 if [ ! -d "$coreClrBinDir" ]; then
354 exit_with_error "$errorSource" "Directory specified by --coreClrBinDir does not exist: $coreClrBinDir"
356 if [ -z "$coreFxBinDir" ]; then
357 exit_with_error "$errorSource" "One of --coreOverlayDir or --coreFxBinDir must be specified." "$printUsage"
361 coreOverlayDir=$testRootDir/Tests/coreoverlay
362 export CORE_ROOT="$coreOverlayDir"
363 if [ -e "$coreOverlayDir" ]; then
364 rm -f -r "$coreOverlayDir"
366 mkdir "$coreOverlayDir"
368 cp -f -v "$coreFxBinDir/"* "$coreOverlayDir/" 2>/dev/null
369 cp -f -p -v "$coreClrBinDir/"* "$coreOverlayDir/" 2>/dev/null
370 if [ -d "$mscorlibDir/bin" ]; then
371 cp -f -v "$mscorlibDir/bin/"* "$coreOverlayDir/" 2>/dev/null
373 cp -f -v "$testDependenciesDir/"xunit* "$coreOverlayDir/" 2>/dev/null
374 cp -n -v "$testDependenciesDir/"* "$coreOverlayDir/" 2>/dev/null
375 if [ -f "$coreOverlayDir/mscorlib.ni.dll" ]; then
376 # Test dependencies come from a Windows build, and mscorlib.ni.dll would be the one from Windows
377 rm -f "$coreOverlayDir/mscorlib.ni.dll"
379 if [ -f "$coreOverlayDir/System.Private.CoreLib.ni.dll" ]; then
380 # Test dependencies come from a Windows build, and System.Private.CoreLib.ni.dll would be the one from Windows
381 rm -f "$coreOverlayDir/System.Private.CoreLib.ni.dll"
383 copy_test_native_bin_to_test_root
386 declare -a skipCrossGenFiles
388 function is_skip_crossgen_test {
389 for skip in "${skipCrossGenFiles[@]}"; do
390 if [ "$1" == "$skip" ]; then
397 function precompile_overlay_assemblies {
398 skipCrossGenFiles=($(read_array "$(dirname "$0")/skipCrossGenFiles.$ARCH.txt"))
400 if [ $doCrossgen == 1 ]; then
401 local overlayDir=$CORE_ROOT
403 filesToPrecompile=$(find -L $overlayDir -iname \*.dll -not -iname \*.ni.dll -not -iname \*-ms-win-\* -type f )
404 for fileToPrecompile in ${filesToPrecompile}
406 local filename=${fileToPrecompile}
407 if [ $jitdisasm == 1 ]; then
408 $overlayDir/corerun $overlayDir/jit-dasm.dll --crossgen $overlayDir/crossgen --platform $overlayDir --output $testRootDir/dasm $filename
410 if [ $exitCode != 0 ]; then
411 echo Unable to generate dasm for $filename
414 if is_skip_crossgen_test "$(basename $filename)"; then
417 echo Precompiling $filename
418 $overlayDir/crossgen /Platform_Assemblies_Paths $overlayDir $filename 1> $filename.stdout 2>$filename.stderr
420 if [[ $exitCode != 0 ]]; then
421 if grep -q -e '0x80131018' $filename.stderr; then
422 printf "\n\t$filename is not a managed assembly.\n\n"
424 echo Unable to precompile $filename.
430 rm $filename.{stdout,stderr}
435 echo Skipping crossgen of FX assemblies.
439 function copy_test_native_bin_to_test_root {
440 local errorSource='copy_test_native_bin_to_test_root'
442 if [ -z "$testNativeBinDir" ]; then
443 exit_with_error "$errorSource" "--testNativeBinDir is required."
445 testNativeBinDir=$testNativeBinDir/src
446 if [ ! -d "$testNativeBinDir" ]; then
447 exit_with_error "$errorSource" "Directory specified by --testNativeBinDir does not exist: $testNativeBinDir"
450 # Copy native test components from the native test build into the respective test directory in the test root directory
451 find "$testNativeBinDir" -type f -iname '*.$libExtension' |
452 while IFS='' read -r filePath || [ -n "$filePath" ]; do
453 local dirPath=$(dirname "$filePath")
454 local destinationDirPath=${testRootDir}${dirPath:${#testNativeBinDir}}
455 if [ ! -d "$destinationDirPath" ]; then
456 exit_with_error "$errorSource" "Cannot copy native test bin '$filePath' to '$destinationDirPath/', as the destination directory does not exist."
458 cp -f "$filePath" "$destinationDirPath/"
462 # Variables for unsupported and failing tests
463 declare -a unsupportedTests
464 declare -a failingTests
465 declare -a playlistTests
466 ((runFailingTestsOnly = 0))
468 # Get an array of items by reading the specified file line by line.
469 function read_array {
472 # bash in Mac OS X doesn't support 'readarray', so using alternate way instead.
473 # readarray -t theArray < "$1"
474 while IFS='' read -r line || [ -n "$line" ]; do
475 theArray[${#theArray[@]}]=$line
480 function load_unsupported_tests {
481 # Load the list of tests that are not supported on this platform. These tests are disabled (skipped) permanently.
482 unsupportedTests=($(read_array "$(dirname "$0")/testsUnsupportedOutsideWindows.txt"))
483 if [ "$ARCH" == "arm" ]; then
484 unsupportedTests+=($(read_array "$(dirname "$0")/testsUnsupportedOnARM32.txt"))
488 function load_failing_tests {
489 # Load the list of tests that fail on this platform. These tests are disabled (skipped) temporarily, pending investigation.
490 failingTests=($(read_array "$(dirname "$0")/testsFailingOutsideWindows.txt"))
492 if [ "$ARCH" == "arm64" ]; then
493 failingTests+=($(read_array "$(dirname "$0")/testsFailingOnArm64.txt"))
497 function load_playlist_tests {
498 # Load the list of tests that are enabled as a part of this test playlist.
499 playlistTests=($(read_array "${playlistFile}"))
502 function is_unsupported_test {
503 for unsupportedTest in "${unsupportedTests[@]}"; do
504 if [ "$1" == "$unsupportedTest" ]; then
511 function is_failing_test {
512 for failingTest in "${failingTests[@]}"; do
513 if [ "$1" == "$failingTest" ]; then
520 function is_playlist_test {
521 for playlistTest in "${playlistTests[@]}"; do
522 if [ "$1" == "$playlistTest" ]; then
529 function skip_unsupported_test {
530 # This function runs in a background process. It should not echo anything, and should not use global variables. This
531 # function is analogous to run_test, and causes the test to be skipped with the message below.
533 local scriptFilePath=$1
534 local outputFilePath=$2
536 echo "Not supported on this platform." >"$outputFilePath"
537 return 2 # skip the test
540 function skip_failing_test {
541 # This function runs in a background process. It should not echo anything, and should not use global variables. This
542 # function is analogous to run_test, and causes the test to be skipped with the message below.
544 local scriptFilePath=$1
545 local outputFilePath=$2
547 echo "Temporarily disabled on this platform due to unexpected failures." >"$outputFilePath"
548 return 2 # skip the test
551 function skip_non_playlist_test {
552 # This function runs in a background process. It should not echo anything, and should not use global variables. This
553 # function is analogous to run_test, and causes the test to be skipped with the message below.
555 local scriptFilePath=$1
556 local outputFilePath=$2
558 echo "Test is not included in the running playlist." >"$outputFilePath"
559 return 2 # skip the test
562 function set_up_core_dump_generation {
563 # We will only enable dump generation here if we're on Mac or Linux
564 if [[ ! ( "$(uname -s)" == "Darwin" || "$(uname -s)" == "Linux" ) ]]; then
568 # We won't enable dump generation on OS X/macOS if the machine hasn't been
569 # configured with the kern.corefile pattern we expect.
570 if [[ ( "$(uname -s)" == "Darwin" && "$(sysctl -n kern.corefile)" != "core.%P" ) ]]; then
571 echo "WARNING: Core dump generation not being enabled due to unexpected kern.corefile value."
575 # Allow dump generation
578 if [ "$(uname -s)" == "Linux" ]; then
579 if [ -e /proc/self/coredump_filter ]; then
580 # Include memory in private and shared file-backed mappings in the dump.
581 # This ensures that we can see disassembly from our shared libraries when
582 # inspecting the contents of the dump. See 'man core' for details.
583 echo 0x3F > /proc/self/coredump_filter
588 function print_info_from_core_file {
591 if [ "$ARCH" == "arm64" ]; then
592 echo "Not inspecting core dumps on arm64 at the moment."
597 local core_file_name=$1
598 local executable_name=$2
600 if ! [ -e $executable_name ]; then
601 echo "Unable to find executable $executable_name"
603 elif ! [ -e $core_file_name ]; then
604 echo "Unable to find core file $core_file_name"
608 # Use LLDB to inspect the core dump on Mac, and GDB everywhere else.
609 if [[ "$OSName" == "Darwin" ]]; then
610 hash lldb 2>/dev/null || { echo >&2 "LLDB was not found. Unable to print core file."; return; }
612 echo "Printing info from core file $core_file_name"
613 lldb -c $core_file_name -b -o 'bt'
615 # Use GDB to print the backtrace from the core file.
616 hash gdb 2>/dev/null || { echo >&2 "GDB was not found. Unable to print core file."; return; }
618 echo "Printing info from core file $core_file_name"
619 gdb --batch -ex "thread apply all bt full" -ex "quit" $executable_name $core_file_name
623 function download_dumpling_script {
624 echo "Downloading latest version of dumpling script."
625 wget "https://dumpling.azurewebsites.net/api/client/dumpling.py"
627 local dumpling_script="dumpling.py"
628 chmod +x $dumpling_script
631 function upload_core_file_to_dumpling {
632 local core_file_name=$1
633 local dumpling_script="dumpling.py"
634 local dumpling_file="local_dumplings.txt"
636 # dumpling requires that the file exist before appending.
637 touch ./$dumpling_file
639 if [ ! -x $dumpling_script ]; then
640 download_dumpling_script
643 if [ ! -x $dumpling_script ]; then
644 echo "Failed to download dumpling script. Dump cannot be uploaded."
648 echo "Uploading $core_file_name to dumpling service."
650 local paths_to_add=""
651 if [ -d "$coreClrBinDir" ]; then
652 echo "Uploading CoreCLR binaries with dump."
653 paths_to_add=$coreClrBinDir
656 # Ensure the script has Unix line endings
657 perl -pi -e 's/\r\n|\n|\r/\n/g' "$dumpling_script"
659 # The output from this will include a unique ID for this dump.
660 ./$dumpling_script "upload" "--dumppath" "$core_file_name" "--incpaths" $paths_to_add "--properties" "Project=CoreCLR" "--squelch" | tee -a $dumpling_file
663 function preserve_core_file {
664 local core_file_name=$1
665 local storage_location="/tmp/coredumps_coreclr"
667 # Create the directory (this shouldn't fail even if it already exists).
668 mkdir -p $storage_location
670 # Only preserve the dump if the directory is empty. Otherwise, do nothing.
671 # This is a way to prevent us from storing/uploading too many dumps.
672 if [ ! "$(ls -A $storage_location)" ]; then
673 echo "Copying core file $core_file_name to $storage_location"
674 cp $core_file_name $storage_location
676 upload_core_file_to_dumpling $core_file_name
680 function inspect_and_delete_core_files {
681 # This function prints some basic information from core files in the current
682 # directory and deletes them immediately. Based on the state of the system, it may
683 # also upload a core file to the dumpling service.
684 # (see preserve_core_file).
686 # Depending on distro/configuration, the core files may either be named "core"
687 # or "core.<PID>" by default. We will read /proc/sys/kernel/core_uses_pid to
688 # determine which one it is.
689 # On OS X/macOS, we checked the kern.corefile value before enabling core dump
690 # generation, so we know it always includes the PID.
691 local core_name_uses_pid=0
692 if [[ (( -e /proc/sys/kernel/core_uses_pid ) && ( "1" == $(cat /proc/sys/kernel/core_uses_pid) ))
693 || ( "$(uname -s)" == "Darwin" ) ]]; then
697 if [ $core_name_uses_pid == "1" ]; then
698 # We don't know what the PID of the process was, so let's look at all core
699 # files whose name matches core.NUMBER
701 [[ $f =~ core.[0-9]+ ]] && print_info_from_core_file "$f" $CORE_ROOT/"corerun" && preserve_core_file "$f" && rm "$f"
703 elif [ -f core ]; then
704 print_info_from_core_file "core" $CORE_ROOT/"corerun"
705 preserve_core_file "core"
711 # This function runs in a background process. It should not echo anything, and should not use global variables.
713 local scriptFilePath=$1
714 local outputFilePath=$2
716 # Switch to directory where the script is
717 cd "$(dirname "$scriptFilePath")"
719 local scriptFileName=$(basename "$scriptFilePath")
720 local outputFileName=$(basename "$outputFilePath")
722 if [ "$limitedCoreDumps" == "ON" ]; then
723 set_up_core_dump_generation
726 "./$scriptFileName" >"$outputFileName" 2>&1
727 local testScriptExitCode=$?
729 # We will try to print some information from generated core dumps if a debugger
730 # is available, and possibly store a dump in a non-transient location.
731 if [ "$limitedCoreDumps" == "ON" ]; then
732 inspect_and_delete_core_files
735 return $testScriptExitCode
738 # Variables for running tests in the background
739 if [ `uname` = "NetBSD" ]; then
740 NumProc=$(getconf NPROCESSORS_ONLN)
741 elif [ `uname` = "Darwin" ]; then
742 NumProc=$(getconf _NPROCESSORS_ONLN)
744 if [ -x "$(command -v nproc)" ]; then
745 NumProc=$(nproc --all)
746 elif [ -x "$(command -v getconf)" ]; then
747 NumProc=$(getconf _NPROCESSORS_ONLN)
752 ((maxProcesses = $NumProc * 3 / 2)) # long tests delay process creation, use a few more processors
755 declare -a scriptFilePaths
756 declare -a outputFilePaths
757 declare -a processIds
758 declare -a testStartTimes
766 for (( i=0; i<$maxProcesses; i++ )); do
767 pid=${processIds[$i]}
768 if [ -z "$pid" ] || [ "$pid" == "$pidNone" ]; then
771 if ! kill -0 $pid 2>/dev/null; then
775 processIds[$i]=$pidNone
783 function get_available_process_index {
786 for (( i=0; i<$maxProcesses; i++ )); do
787 pid=${processIds[$i]}
788 if [ -z "$pid" ] || [ "$pid" == "$pidNone" ]; then
795 function finish_test {
797 local testScriptExitCode=$?
798 local finishedProcessIndex=$waitProcessIndex
801 local scriptFilePath=${scriptFilePaths[$finishedProcessIndex]}
802 local outputFilePath=${outputFilePaths[$finishedProcessIndex]}
803 local scriptFileName=$(basename "$scriptFilePath")
806 local testRunningTime=
809 if ((verbose == 1)); then
810 header=$(printf "[%4d]" $countTotalTests)
813 if [ "$showTime" == "ON" ]; then
814 testEndTime=$(date +%s)
815 testRunningTime=$(( $testEndTime - ${testStartTimes[$finishedProcessIndex]} ))
816 header=$header$(printf "[%4ds]" $testRunningTime)
819 local xunitTestResult
820 case $testScriptExitCode in
822 let countPassedTests++
823 xunitTestResult='Pass'
824 if ((verbose == 1 || runFailingTestsOnly == 1)); then
825 echo "PASSED - ${header}${scriptFilePath}"
827 echo " - ${header}${scriptFilePath}"
831 let countSkippedTests++
832 xunitTestResult='Skip'
833 echo "SKIPPED - ${header}${scriptFilePath}"
836 let countFailedTests++
837 xunitTestResult='Fail'
838 echo "FAILED - ${header}${scriptFilePath}"
841 let countTotalTests++
843 if ((verbose == 1 || testScriptExitCode != 0)); then
844 while IFS='' read -r line || [ -n "$line" ]; do
846 done <"$outputFilePath"
849 xunit_output_add_test "$scriptFilePath" "$outputFilePath" "$xunitTestResult" "$testScriptExitCode" "$testRunningTime"
852 function finish_remaining_tests {
853 # Finish the remaining tests in the order in which they were started
854 while ((processCount > 0)); do
860 local scriptFilePath=$1
861 local scriptFileDir=$(dirname "$scriptFilePath")
863 test "$verbose" == 1 && echo "Preparing $scriptFilePath"
865 if [ ! "$noLFConversion" == "ON" ]; then
866 # Convert DOS line endings to Unix if needed
867 perl -pi -e 's/\r\n|\n|\r/\n/g' "$scriptFilePath"
870 # Add executable file mode bit if needed
871 chmod +x "$scriptFilePath"
873 #remove any NI and Locks
874 rm -f $scriptFileDir/*.ni.*
875 rm -rf $scriptFileDir/lock
878 function start_test {
879 local nextProcessIndex=$(get_available_process_index)
880 local scriptFilePath=$1
881 if ((runFailingTestsOnly == 1)) && ! is_failing_test "$scriptFilePath"; then
885 # Skip any test that's not in the current playlist, if a playlist was
887 if [ -n "$playlistFile" ] && ! is_playlist_test "$scriptFilePath"; then
891 if ((nextProcessIndex == maxProcesses)); then
893 nextProcessIndex=$(get_available_process_index)
896 scriptFilePaths[$nextProcessIndex]=$scriptFilePath
897 local scriptFileName=$(basename "$scriptFilePath")
898 local outputFilePath=$(dirname "$scriptFilePath")/${scriptFileName}.out
899 outputFilePaths[$nextProcessIndex]=$outputFilePath
901 if [ "$showTime" == "ON" ]; then
902 testStartTimes[$nextProcessIndex]=$(date +%s)
905 test "$verbose" == 1 && echo "Starting $scriptFilePath"
906 if is_unsupported_test "$scriptFilePath"; then
907 skip_unsupported_test "$scriptFilePath" "$outputFilePath" &
908 elif ((runFailingTestsOnly == 0)) && is_failing_test "$scriptFilePath"; then
909 skip_failing_test "$scriptFilePath" "$outputFilePath" &
911 run_test "$scriptFilePath" "$outputFilePath" &
913 processIds[$nextProcessIndex]=$!
918 # Get a list of directories in which to scan for tests by reading the
919 # specified file line by line.
920 function set_test_directories {
921 local errorSource='set_test_directories'
923 local listFileName=$1
925 if [ ! -f "$listFileName" ]
927 exit_with_error "$errorSource" "Test directories file not found at $listFileName"
929 testDirectories=($(read_array "$listFileName"))
932 function run_tests_in_directory {
935 # Recursively search through directories for .sh files to prepare them.
936 # Note: This needs to occur before any test runs as some of the .sh files
937 # depend on other .sh files
938 for scriptFilePath in $(find "$testDir" -type f -iname '*.sh' | sort)
940 prep_test "${scriptFilePath:2}"
942 echo "The tests have been prepared"
943 # Recursively search through directories for .sh files to run.
944 for scriptFilePath in $(find "$testDir" -type f -iname '*.sh' | sort)
946 start_test "${scriptFilePath:2}"
950 function coreclr_code_coverage {
951 local coverageDir="$coverageOutputDir/Coverage"
952 local toolsDir="$coverageOutputDir/Coverage/tools"
953 local reportsDir="$coverageOutputDir/Coverage/reports"
954 local packageName="unix-code-coverage-tools.1.0.0.nupkg"
957 mkdir -p $coverageDir
960 pushd $toolsDir > /dev/null
962 echo "Pulling down code coverage tools"
963 wget -q https://www.myget.org/F/dotnet-buildtools/api/v2/package/unix-code-coverage-tools/1.0.0 -O $packageName
964 echo "Unzipping to $toolsDir"
965 unzip -q -o $packageName
969 chmod a+rwx ./$OSName/llvm-cov
972 echo "Generating coreclr code coverage reports at $reportsDir/coreclr.html"
973 echo "./gcovr $coreClrObjs --gcov-executable=$toolsDir/$OS/llvm-cov -r $coreClrSrc --html --html-details -o $reportsDir/coreclr.html"
975 ./gcovr $coreClrObjs --gcov-executable=$toolsDir/$OSName/llvm-cov -r $coreClrSrc --html --html-details -o $reportsDir/coreclr.html
981 function check_cpu_architecture {
982 local CPUName=$(uname -m)
999 echo "Unknown CPU $CPUName detected, configuring as if for x64"
1007 ARCH=$(check_cpu_architecture)
1008 echo "Running on CPU- $ARCH"
1010 # Exit code constants
1011 readonly EXIT_CODE_SUCCESS=0 # Script ran normally.
1012 readonly EXIT_CODE_EXCEPTION=1 # Script exited because something exceptional happened (e.g. bad arguments, Ctrl-C interrupt).
1013 readonly EXIT_CODE_TEST_FAILURE=2 # Script completed successfully, but one or more tests failed.
1015 # Argument variables
1034 ((disableEventLogging = 0))
1048 exit $EXIT_CODE_SUCCESS
1057 export COMPlus_JitStress=${i#*=}
1060 export COMPlus_JitStressRegs=${i#*=}
1063 export COMPlus_JITMinOpts=1
1066 export COMPlus_ForceRelocs=1
1069 export ILLINK=${i#*=}
1072 --tieredcompilation)
1073 export COMPlus_EXPERIMENTAL_TieredCompilation=1
1079 ((ilasmroundtrip = 1))
1084 --testNativeBinDir=*)
1085 testNativeBinDir=${i#*=}
1088 coreOverlayDir=${i#*=}
1091 coreClrBinDir=${i#*=}
1097 coreFxBinDir=${i#*=}
1100 testDirectories[${#testDirectories[@]}]=${i#*=}
1103 set_test_directories "${i#*=}"
1105 --runFailingTestsOnly)
1106 ((runFailingTestsOnly = 1))
1108 --disableEventLogging)
1109 ((disableEventLogging = 1))
1112 export RunCrossGen=1
1115 ((maxProcesses = 1))
1127 playlistFile=${i#*=}
1138 --coverage-output-dir=*)
1139 coverageOutputDir=${i#*=}
1145 export COMPlus_GCStress=${i#*=}
1148 export COMPlus_GCName=${i#*=}
1156 --build-overlay-only)
1159 --limitedDumpGeneration)
1162 --xunitOutputPath=*)
1163 xunitOutputPath=${i#*=}
1166 echo "Unknown switch: $i"
1168 exit $EXIT_CODE_SUCCESS
1173 if [ -n "$coreOverlayDir" ] && [ "$buildOverlayOnly" == "ON" ]; then
1174 echo "Can not use \'--coreOverlayDir=<path>\' and \'--build-overlay-only\' at the same time."
1175 exit $EXIT_CODE_EXCEPTION
1178 if ((disableEventLogging == 0)); then
1179 export COMPlus_EnableEventLog=1
1182 export CORECLR_SERVER_GC="$serverGC"
1184 if [ -z "$testRootDir" ]; then
1185 echo "--testRootDir is required."
1187 exit $EXIT_CODE_EXCEPTION
1189 if [ ! -d "$testRootDir" ]; then
1190 echo "Directory specified by --testRootDir does not exist: $testRootDir"
1191 exit $EXIT_CODE_EXCEPTION
1194 # Copy native interop test libraries over to the mscorlib path in
1195 # order for interop tests to run on linux.
1196 if [ -z "$mscorlibDir" ]; then
1197 mscorlibDir=$coreClrBinDir
1200 if [ ! -z "$longgc" ]; then
1201 echo "Running Long GC tests"
1202 export RunningLongGCTests=1
1205 if [ ! -z "$gcsimulator" ]; then
1206 echo "Running GC simulator tests"
1207 export RunningGCSimulatorTests=1
1210 if [[ ! "$jitdisasm" -eq 0 ]]; then
1211 echo "Running jit disasm"
1212 export RunningJitDisasm=1
1215 if [ ! -z "$ilasmroundtrip" ]; then
1216 echo "Running Ilasm round trip"
1217 export RunningIlasmRoundTrip=1
1220 # If this is a coverage run, make sure the appropriate args have been passed
1221 if [ "$CoreClrCoverage" == "ON" ]
1223 echo "Code coverage is enabled for this run"
1225 if [ ! "$OSName" == "Darwin" ] && [ ! "$OSName" == "Linux" ]
1227 echo "Code Coverage not supported on $OS"
1231 if [ -z "$coreClrObjs" ]
1233 echo "Coreclr obj files are required to generate code coverage reports"
1234 echo "Coreclr obj files root path can be passed using '--coreclr-obj' argument"
1238 if [ -z "$coreClrSrc" ]
1240 echo "Coreclr src files are required to generate code coverage reports"
1241 echo "Coreclr src files root path can be passed using '--coreclr-src' argument"
1245 if [ -z "$coverageOutputDir" ]
1247 echo "Output directory for coverage results must be specified"
1248 echo "Output path can be specified '--coverage-output-dir' argument"
1255 precompile_overlay_assemblies
1257 if [ "$buildOverlayOnly" == "ON" ];
1259 echo "Build overlay directory '$coreOverlayDir' complete."
1263 if [ -n "$playlistFile" ]
1265 # Use a playlist file exclusively, if it was provided
1266 echo "Executing playlist $playlistFile"
1269 load_unsupported_tests
1273 # Other architectures are not supported yet.
1274 if [ "$ARCH" == "x64" ]
1276 scriptPath=$(dirname $0)
1277 ${scriptPath}/setup-stress-dependencies.sh --outputDir=$coreOverlayDir
1279 if [ "$ARCH" != "arm64" ]
1281 echo "Skip preparing for GC stress test. Dependent package is not supported on this architecture."
1285 export __TestEnv=$testEnv
1289 dumplingsListPath="$testRootDir/dumplings.txt"
1291 # clean up any existing dumpling remnants from previous runs.
1292 rm -f "$dumplingsListPath"
1293 find $testRootDir -type f -name "local_dumplings.txt" -exec rm {} \;
1295 time_start=$(date +"%s")
1296 if [ -z "$testDirectories" ]
1298 # No test directories were specified, so run everything in the current
1299 # directory and its subdirectories.
1300 run_tests_in_directory "."
1302 # Otherwise, run all the tests in each specified test directory.
1303 for testDir in "${testDirectories[@]}"
1305 if [ ! -d "$testDir" ]; then
1306 echo "Test directory does not exist: $testDir"
1308 run_tests_in_directory "./$testDir"
1312 finish_remaining_tests
1316 find $testRootDir -type f -name "local_dumplings.txt" -exec cat {} \; > $dumplingsListPath
1318 if [ -s $dumplingsListPath ]; then
1319 cat $dumplingsListPath
1321 rm $dumplingsListPath
1324 time_end=$(date +"%s")
1325 time_diff=$(($time_end-$time_start))
1326 echo "$(($time_diff / 60)) minutes and $(($time_diff % 60)) seconds taken to run CoreCLR tests."
1330 if [ "$CoreClrCoverage" == "ON" ]
1332 coreclr_code_coverage
1335 if ((countFailedTests > 0)); then
1336 exit $EXIT_CODE_TEST_FAILURE
1339 exit $EXIT_CODE_SUCCESS