Merge pull request #4789 from sejongoh/always_download_coredistools
[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 '    --coreClrBinDir="coreclr/bin/Product/Linux.x64.Debug"'
13     echo '    --mscorlibDir="windows/coreclr/bin/Product/Linux.x64.Debug"'
14     echo '    --coreFxBinDir="corefx/bin/Linux.AnyCPU.Debug"'
15     echo '    --coreFxNativeBinDir="corefx/bin/Linux.x64.Debug"'
16     echo ''
17     echo 'Required arguments:'
18     echo '  --testRootDir=<path>             : Root directory of the test build (e.g. coreclr/bin/tests/Windows_NT.x64.Debug).'
19     echo '  --testNativeBinDir=<path>        : Directory of the native CoreCLR test build (e.g. coreclr/bin/obj/Linux.x64.Debug/tests).'
20     echo '  (Also required: Either --coreOverlayDir, or all of the switches --coreOverlayDir overrides)'
21     echo ''
22     echo 'Optional arguments:'
23     echo '  --coreOverlayDir=<path>          : Directory containing core binaries and test dependencies. If not specified, the'
24     echo '                                     default is testRootDir/Tests/coreoverlay. This switch overrides --coreClrBinDir,'
25     echo '                                     --mscorlibDir, --coreFxBinDir, and --coreFxNativeBinDir.'
26     echo '  --coreClrBinDir=<path>           : Directory of the CoreCLR build (e.g. coreclr/bin/Product/Linux.x64.Debug).'
27     echo '  --mscorlibDir=<path>             : Directory containing the built mscorlib.dll. If not specified, it is expected to be'
28     echo '                                       in the directory specified by --coreClrBinDir.'
29     echo '  --coreFxBinDir="<path>[;<path>]" : List of one or more directories with CoreFX build outputs (semicolon-delimited)'
30     echo '                                     (e.g. "corefx/bin/Linux.AnyCPU.Debug;corefx/bin/Unix.AnyCPU.Debug;corefx/bin/AnyOS.AnyCPU.Debug").'
31     echo '                                     If files with the same name are present in multiple directories, the first one wins.'
32     echo '  --coreFxNativeBinDir=<path>      : Directory of the CoreFX native build (e.g. corefx/bin/Linux.x64.Debug).'
33     echo '  --testDir=<path>                 : Run tests only in the specified directory. The path is relative to the directory'
34     echo '                                     specified by --testRootDir. Multiple of this switch may be specified.'
35     echo '  --testDirFile=<path>             : Run tests only in the directories specified by the file at <path>. Paths are listed'
36     echo '                                     one line, relative to the directory specified by --testRootDir.'
37     echo '  --runFailingTestsOnly            : Run only the tests that are disabled on this platform due to unexpected failures.'
38     echo '                                     Failing tests are listed in coreclr/tests/failingTestsOutsideWindows.txt, one per'
39     echo '                                     line, as paths to .sh files relative to the directory specified by --testRootDir.'
40     echo '  --disableEventLogging            : Disable the events logged by both VM and Managed Code'
41     echo '  --sequential                     : Run tests sequentially (default is to run in parallel).'
42     echo '  --playlist=<path>                : Run only the tests that are specified in the file at <path>, in the same format as'
43     echo '                                     runFailingTestsOnly'
44     echo '  -v, --verbose                    : Show output from each test.'
45     echo '  -h|--help                        : Show usage information.'
46     echo '  --useServerGC                    : Enable server GC for this test run'
47     echo '  --test-en                        : Script to set environment variables for tests'
48     echo ''
49     echo 'Runtime Code Coverage options:'
50     echo '  --coreclr-coverage               : Optional argument to get coreclr code coverage reports'
51     echo '  --coreclr-objs=<path>            : Location of root of the object directory'
52     echo '                                     containing the linux/mac coreclr build'
53     echo '  --coreclr-src=<path>             : Location of root of the directory'
54     echo '                                     containing the coreclr source files'
55     echo '  --coverage-output-dir=<path>     : Directory where coverage output will be written to'
56     echo ''
57 }
58
59 function print_results {
60     echo ""
61     echo "======================="
62     echo "     Test Results"
63     echo "======================="
64     echo "# Tests Discovered : $countTotalTests"
65     echo "# Passed           : $countPassedTests"
66     echo "# Failed           : $countFailedTests"
67     echo "# Skipped          : $countSkippedTests"
68     echo "======================="
69 }
70
71 # Initialize counters for bookkeeping.
72 countTotalTests=0
73 countPassedTests=0
74 countFailedTests=0
75 countSkippedTests=0
76
77 # Variables for xUnit-style XML output. XML format: https://xunit.github.io/docs/format-xml-v2.html
78 xunitOutputPath=
79 xunitTestOutputPath=
80
81 # libExtension determines extension for dynamic library files
82 OSName=$(uname -s)
83 libExtension=
84 case $OSName in
85     Darwin)
86         libExtension="dylib"
87         ;;
88
89     Linux)
90         libExtension="so"
91         ;;
92
93     NetBSD)
94         libExtension="so"
95         ;;
96
97     *)
98         echo "Unsupported OS $OSName detected, configuring as if for Linux"
99         libExtension="so"
100         ;;
101 esac
102
103
104 function xunit_output_begin {
105     xunitOutputPath=$testRootDir/coreclrtests.xml
106     xunitTestOutputPath=${xunitOutputPath}.test
107     if [ -e "$xunitOutputPath" ]; then
108         rm -f -r "$xunitOutputPath"
109     fi
110     if [ -e "$xunitTestOutputPath" ]; then
111         rm -f -r "$xunitTestOutputPath"
112     fi
113 }
114
115 function xunit_output_add_test {
116     # <assemblies>
117     #   <assembly>
118     #     <collection>
119     #       <test .../> <!-- Write this element here -->
120
121     local scriptFilePath=$1
122     local outputFilePath=$2
123     local testResult=$3 # Pass, Fail, or Skip
124     local testScriptExitCode=$4
125
126     local testPath=${scriptFilePath%.sh} # Remove trailing ".sh"
127     local testDir=$(dirname "$testPath")
128     local testName=$(basename "$testPath")
129
130     # Replace '/' with '.'
131     testPath=$(echo "$testPath" | tr / .)
132     testDir=$(echo "$testDir" | tr / .)
133
134     local line
135
136     line="      "
137     line="${line}<test"
138     line="${line} name=\"${testPath}\""
139     line="${line} type=\"${testDir}\""
140     line="${line} method=\"${testName}\""
141     line="${line} result=\"${testResult}\""
142
143     if [ "$testResult" == "Pass" ]; then
144         line="${line}/>"
145         echo "$line" >>"$xunitTestOutputPath"
146         return
147     fi
148
149     line="${line}>"
150     echo "$line" >>"$xunitTestOutputPath"
151
152     line="        "
153     if [ "$testResult" == "Skip" ]; then
154         line="${line}<reason><![CDATA[$(cat "$outputFilePath")]]></reason>"
155         echo "$line" >>"$xunitTestOutputPath"
156     else
157         line="${line}<failure exception-type=\"Exit code: ${testScriptExitCode}\">"
158         echo "$line" >>"$xunitTestOutputPath"
159
160         line="          "
161         line="${line}<message>"
162         echo "$line" >>"$xunitTestOutputPath"
163         line="            "
164         line="${line}<![CDATA["
165         echo "$line" >>"$xunitTestOutputPath"
166         cat "$outputFilePath" >>"$xunitTestOutputPath"
167         line="            "
168         line="${line}]]>"
169         echo "$line" >>"$xunitTestOutputPath"
170         line="          "
171         line="${line}</message>"
172         echo "$line" >>"$xunitTestOutputPath"
173
174         line="        "
175         line="${line}</failure>"
176         echo "$line" >>"$xunitTestOutputPath"
177     fi
178
179     line="      "
180     line="${line}</test>"
181     echo "$line" >>"$xunitTestOutputPath"
182 }
183
184 function xunit_output_end {
185     local errorSource=$1
186     local errorMessage=$2
187
188     local errorCount
189     if [ -z "$errorSource" ]; then
190         ((errorCount = 0))
191     else
192         ((errorCount = 1))
193     fi
194
195     echo '<?xml version="1.0" encoding="utf-8"?>' >>"$xunitOutputPath"
196     echo '<assemblies>' >>"$xunitOutputPath"
197
198     local line
199
200     # <assembly ...>
201     line="  "
202     line="${line}<assembly"
203     line="${line} name=\"CoreClrTestAssembly\""
204     line="${line} total=\"${countTotalTests}\""
205     line="${line} passed=\"${countPassedTests}\""
206     line="${line} failed=\"${countFailedTests}\""
207     line="${line} skipped=\"${countSkippedTests}\""
208     line="${line} errors=\"${errorCount}\""
209     line="${line}>"
210     echo "$line" >>"$xunitOutputPath"
211
212     # <collection ...>
213     line="    "
214     line="${line}<collection"
215     line="${line} name=\"CoreClrTestCollection\""
216     line="${line} total=\"${countTotalTests}\""
217     line="${line} passed=\"${countPassedTests}\""
218     line="${line} failed=\"${countFailedTests}\""
219     line="${line} skipped=\"${countSkippedTests}\""
220     line="${line}>"
221     echo "$line" >>"$xunitOutputPath"
222
223     # <test .../> <test .../> ...
224     if [ -f "$xunitTestOutputPath" ]; then
225         cat "$xunitTestOutputPath" >>"$xunitOutputPath"
226         rm -f "$xunitTestOutputPath"
227     fi
228
229     # </collection>
230     line="    "
231     line="${line}</collection>"
232     echo "$line" >>"$xunitOutputPath"
233
234     if [ -n "$errorSource" ]; then
235         # <errors>
236         line="    "
237         line="${line}<errors>"
238         echo "$line" >>"$xunitOutputPath"
239
240         # <error ...>
241         line="      "
242         line="${line}<error"
243         line="${line} type=\"TestHarnessError\""
244         line="${line} name=\"${errorSource}\""
245         line="${line}>"
246         echo "$line" >>"$xunitOutputPath"
247
248         # <failure .../>
249         line="        "
250         line="${line}<failure>${errorMessage}</failure>"
251         echo "$line" >>"$xunitOutputPath"
252
253         # </error>
254         line="      "
255         line="${line}</error>"
256         echo "$line" >>"$xunitOutputPath"
257
258         # </errors>
259         line="    "
260         line="${line}</errors>"
261         echo "$line" >>"$xunitOutputPath"
262     fi
263
264     # </assembly>
265     line="  "
266     line="${line}</assembly>"
267     echo "$line" >>"$xunitOutputPath"
268
269     # </assemblies>
270     echo '</assemblies>' >>"$xunitOutputPath"
271 }
272
273 function exit_with_error {
274     local errorSource=$1
275     local errorMessage=$2
276     local printUsage=$3
277
278     if [ -z "$printUsage" ]; then
279         ((printUsage = 0))
280     fi
281
282     echo "$errorMessage"
283     xunit_output_end "$errorSource" "$errorMessage"
284     if ((printUsage != 0)); then
285         print_usage
286     fi
287     exit $EXIT_CODE_EXCEPTION
288 }
289
290 # Handle Ctrl-C. We will stop execution and print the results that
291 # we gathered so far.
292 function handle_ctrl_c {
293     local errorSource='handle_ctrl_c'
294
295     echo ""
296     echo "*** Stopping... ***"
297     print_results
298     exit_with_error "$errorSource" "Test run aborted by Ctrl+C."
299 }
300
301 # Register the Ctrl-C handler
302 trap handle_ctrl_c INT
303
304 function create_core_overlay {
305     local errorSource='create_core_overlay'
306     local printUsage=1
307
308     if [ -n "$coreOverlayDir" ]; then
309         export CORE_ROOT="$coreOverlayDir"
310         return
311     fi
312
313     # Check inputs to make sure we have enough information to create the core layout. $testRootDir/Tests/Core_Root should
314     # already exist and contain test dependencies that are not built.
315     local testDependenciesDir=$testRootDir/Tests/Core_Root
316     if [ ! -d "$testDependenciesDir" ]; then
317         exit_with_error "$errorSource" "Did not find the test dependencies directory: $testDependenciesDir"
318     fi
319     if [ -z "$coreClrBinDir" ]; then
320         exit_with_error "$errorSource" "One of --coreOverlayDir or --coreClrBinDir must be specified." "$printUsage"
321     fi
322     if [ ! -d "$coreClrBinDir" ]; then
323         exit_with_error "$errorSource" "Directory specified by --coreClrBinDir does not exist: $coreClrBinDir"
324     fi
325     if [ ! -f "$mscorlibDir/mscorlib.dll" ]; then
326         exit_with_error "$errorSource" "mscorlib.dll was not found in: $mscorlibDir"
327     fi
328     if [ -z "$coreFxBinDir" ]; then
329         exit_with_error "$errorSource" "One of --coreOverlayDir or --coreFxBinDir must be specified." "$printUsage"
330     fi
331     if [ -z "$coreFxNativeBinDir" ]; then
332         exit_with_error "$errorSource" "One of --coreOverlayDir or --coreFxBinDir must be specified." "$printUsage"
333     fi
334     if [ ! -d "$coreFxNativeBinDir/Native" ]; then
335         exit_with_error "$errorSource" "Directory specified by --coreFxBinDir does not exist: $coreFxNativeBinDir/Native"
336     fi
337
338     # Create the overlay
339     coreOverlayDir=$testRootDir/Tests/coreoverlay
340     export CORE_ROOT="$coreOverlayDir"
341     if [ -e "$coreOverlayDir" ]; then
342         rm -f -r "$coreOverlayDir"
343     fi
344     mkdir "$coreOverlayDir"
345
346     while IFS=';' read -ra coreFxBinDirectories; do
347         for currDir in "${coreFxBinDirectories[@]}"; do
348             if [ ! -d "$currDir" ]; then
349                 exit_with_error "$errorSource" "Directory specified in --coreFxBinDir does not exist: $currDir"
350             fi
351
352             (cd $currDir && find . -iname '*.dll' \! -iwholename '*test*' \! -iwholename '*/ToolRuntime/*' \! -iwholename '*/RemoteExecutorConsoleApp/*' \! -iwholename '*/net*' \! -iwholename '*aot*' -exec cp -n '{}' "$coreOverlayDir/" \;)
353         done
354     done <<< $coreFxBinDir
355
356     cp -f "$coreFxNativeBinDir/Native/"*."$libExtension" "$coreOverlayDir/" 2>/dev/null
357
358     cp -f "$coreClrBinDir/"* "$coreOverlayDir/" 2>/dev/null
359     cp -f "$mscorlibDir/mscorlib.dll" "$coreOverlayDir/"
360     cp -n "$testDependenciesDir"/* "$coreOverlayDir/" 2>/dev/null
361     if [ -f "$coreOverlayDir/mscorlib.ni.dll" ]; then
362         # Test dependencies come from a Windows build, and mscorlib.ni.dll would be the one from Windows
363         rm -f "$coreOverlayDir/mscorlib.ni.dll"
364     fi
365 }
366
367 function precompile_overlay_assemblies {
368
369     if [ $doCrossgen == 1 ]; then
370     
371         local overlayDir=$CORE_ROOT
372         
373         filesToPrecompile=$(ls -trh $overlayDir/*.dll)
374         for fileToPrecompile in ${filesToPrecompile}
375         do
376             local filename=${fileToPrecompile}
377             # Precompile any assembly except mscorlib since we already have its NI image available.
378             if [[ "$filename" != *"mscorlib.dll"* ]]; then
379                 if [[ "$filename" != *"mscorlib.ni.dll"* ]]; then
380                     echo Precompiling $filename
381                     $overlayDir/crossgen /Platform_Assemblies_Paths $overlayDir $filename 2>/dev/null
382                     local exitCode=$?
383                     if [ $exitCode == -2146230517 ]; then
384                         echo $filename is not a managed assembly.    
385                     elif [ $exitCode != 0 ]; then
386                         echo Unable to precompile $filename.
387                     else
388                         echo Successfully precompiled $filename
389                     fi
390                 fi
391             fi
392         done
393     else
394         echo Skipping crossgen of FX assemblies.
395     fi    
396 }
397
398 function copy_test_native_bin_to_test_root {
399     local errorSource='copy_test_native_bin_to_test_root'
400
401     if [ -z "$testNativeBinDir" ]; then
402         exit_with_error "$errorSource" "--testNativeBinDir is required."
403     fi
404     testNativeBinDir=$testNativeBinDir/src
405     if [ ! -d "$testNativeBinDir" ]; then
406         exit_with_error "$errorSource" "Directory specified by --testNativeBinDir does not exist: $testNativeBinDir"
407     fi
408
409     # Copy native test components from the native test build into the respective test directory in the test root directory
410     find "$testNativeBinDir" -type f -iname '*.$libExtension' |
411         while IFS='' read -r filePath || [ -n "$filePath" ]; do
412             local dirPath=$(dirname "$filePath")
413             local destinationDirPath=${testRootDir}${dirPath:${#testNativeBinDir}}
414             if [ ! -d "$destinationDirPath" ]; then
415                 exit_with_error "$errorSource" "Cannot copy native test bin '$filePath' to '$destinationDirPath/', as the destination directory does not exist."
416             fi
417             cp -f "$filePath" "$destinationDirPath/"
418         done
419 }
420
421 # Variables for unsupported and failing tests
422 declare -a unsupportedTests
423 declare -a failingTests
424 declare -a playlistTests
425 ((runFailingTestsOnly = 0))
426
427 # Get an array of items by reading the specified file line by line.
428 function read_array {
429     local theArray=()
430
431     # bash in Mac OS X doesn't support 'readarray', so using alternate way instead.
432     # readarray -t theArray < "$1"
433     while IFS='' read -r line || [ -n "$line" ]; do
434         theArray[${#theArray[@]}]=$line
435     done < "$1"
436     echo ${theArray[@]}
437 }
438
439 function load_unsupported_tests {
440     # Load the list of tests that are not supported on this platform. These tests are disabled (skipped) permanently.
441     unsupportedTests=($(read_array "$(dirname "$0")/testsUnsupportedOutsideWindows.txt"))
442 }
443
444 function load_failing_tests {
445     # Load the list of tests that fail on this platform. These tests are disabled (skipped) temporarily, pending investigation.
446     failingTests=($(read_array "$(dirname "$0")/testsFailingOutsideWindows.txt"))
447 }
448
449 function load_playlist_tests {
450     # Load the list of tests that are enabled as a part of this test playlist.
451     playlistTests=($(read_array "${playlistFile}"))
452 }
453
454 function is_unsupported_test {
455     for unsupportedTest in "${unsupportedTests[@]}"; do
456         if [ "$1" == "$unsupportedTest" ]; then
457             return 0
458         fi
459     done
460     return 1
461 }
462
463 function is_failing_test {
464     for failingTest in "${failingTests[@]}"; do
465         if [ "$1" == "$failingTest" ]; then
466             return 0
467         fi
468     done
469     return 1
470 }
471
472 function is_playlist_test {
473     for playlistTest in "${playlistTests[@]}"; do
474         if [ "$1" == "$playlistTest" ]; then
475             return 0
476         fi
477     done
478     return 1
479 }
480
481 function skip_unsupported_test {
482     # This function runs in a background process. It should not echo anything, and should not use global variables. This
483     # function is analogous to run_test, and causes the test to be skipped with the message below.
484
485     local scriptFilePath=$1
486     local outputFilePath=$2
487
488     echo "Not supported on this platform." >"$outputFilePath"
489     return 2 # skip the test
490 }
491
492 function skip_failing_test {
493     # This function runs in a background process. It should not echo anything, and should not use global variables. This
494     # function is analogous to run_test, and causes the test to be skipped with the message below.
495
496     local scriptFilePath=$1
497     local outputFilePath=$2
498
499     echo "Temporarily disabled on this platform due to unexpected failures." >"$outputFilePath"
500     return 2 # skip the test
501 }
502
503 function skip_non_playlist_test {
504     # This function runs in a background process. It should not echo anything, and should not use global variables. This
505     # function is analogous to run_test, and causes the test to be skipped with the message below.
506
507     local scriptFilePath=$1
508     local outputFilePath=$2
509
510     echo "Test is not included in the running playlist." >"$outputFilePath"
511     return 2 # skip the test
512 }
513
514 function run_test {
515     # This function runs in a background process. It should not echo anything, and should not use global variables.
516
517     local scriptFilePath=$1
518     local outputFilePath=$2
519
520     # Switch to directory where the script is
521     cd "$(dirname "$scriptFilePath")"
522
523     local scriptFileName=$(basename "$scriptFilePath")
524     local outputFileName=$(basename "$outputFilePath")
525
526     # Convert DOS line endings to Unix if needed
527     perl -pi -e 's/\r\n|\n|\r/\n/g' "$scriptFileName"
528     
529     # Add executable file mode bit if needed
530     chmod +x "$scriptFileName"
531
532     "./$scriptFileName" >"$outputFileName" 2>&1
533     return $?
534 }
535
536 # Variables for running tests in the background
537 if [ `uname` = "NetBSD" ]; then
538     NumProc=$(getconf NPROCESSORS_ONLN)
539 else
540     NumProc=$(getconf _NPROCESSORS_ONLN)
541 fi
542 ((maxProcesses = $NumProc * 3 / 2)) # long tests delay process creation, use a few more processors
543
544 ((nextProcessIndex = 0))
545 ((processCount = 0))
546 declare -a scriptFilePaths
547 declare -a outputFilePaths
548 declare -a processIds
549
550 function finish_test {
551     wait ${processIds[$nextProcessIndex]}
552     local testScriptExitCode=$?
553     ((--processCount))
554
555     local scriptFilePath=${scriptFilePaths[$nextProcessIndex]}
556     local outputFilePath=${outputFilePaths[$nextProcessIndex]}
557     local scriptFileName=$(basename "$scriptFilePath")
558
559     local xunitTestResult
560     case $testScriptExitCode in
561         0)
562             let countPassedTests++
563             xunitTestResult='Pass'
564             if ((verbose == 1 || runFailingTestsOnly == 1)); then
565                 echo "PASSED   - $scriptFilePath"
566             else
567                 echo "         - $scriptFilePath"
568             fi
569             ;;
570         2)
571             let countSkippedTests++
572             xunitTestResult='Skip'
573             echo "SKIPPED  - $scriptFilePath"
574             ;;
575         *)
576             let countFailedTests++
577             xunitTestResult='Fail'
578             echo "FAILED   - $scriptFilePath"
579             ;;
580     esac
581     let countTotalTests++
582
583     if ((verbose == 1 || testScriptExitCode != 0)); then
584         while IFS='' read -r line || [ -n "$line" ]; do
585             echo "               $line"
586         done <"$outputFilePath"
587     fi
588
589     xunit_output_add_test "$scriptFilePath" "$outputFilePath" "$xunitTestResult" "$testScriptExitCode"
590 }
591
592 function finish_remaining_tests {
593     # Finish the remaining tests in the order in which they were started
594     if ((nextProcessIndex >= processCount)); then
595         ((nextProcessIndex = 0))
596     fi
597     while ((processCount > 0)); do
598         finish_test
599         ((nextProcessIndex = (nextProcessIndex + 1) % maxProcesses))
600     done
601     ((nextProcessIndex = 0))
602 }
603
604 function prep_test {
605     local scriptFilePath=$1
606
607     test "$verbose" == 1 && echo "Preparing $scriptFilePath"
608
609     # Convert DOS line endings to Unix if needed
610     perl -pi -e 's/\r\n|\n|\r/\n/g' "$scriptFilePath"
611     
612     # Add executable file mode bit if needed
613     chmod +x "$scriptFilePath"
614
615 }
616
617 function start_test {
618     local scriptFilePath=$1
619     if ((runFailingTestsOnly == 1)) && ! is_failing_test "$scriptFilePath"; then
620         return
621     fi
622     
623     # Skip any test that's not in the current playlist, if a playlist was
624     # given to us.
625     if [ -n "$playlistFile" ] && ! is_playlist_test "$scriptFilePath"; then
626         return
627     fi
628
629     if ((nextProcessIndex < processCount)); then
630         finish_test
631     fi
632
633     scriptFilePaths[$nextProcessIndex]=$scriptFilePath
634     local scriptFileName=$(basename "$scriptFilePath")
635     local outputFilePath=$(dirname "$scriptFilePath")/${scriptFileName}.out
636     outputFilePaths[$nextProcessIndex]=$outputFilePath
637
638     test "$verbose" == 1 && echo "Starting $scriptFilePath"
639     if is_unsupported_test "$scriptFilePath"; then
640         skip_unsupported_test "$scriptFilePath" "$outputFilePath" &
641     elif ((runFailingTestsOnly == 0)) && is_failing_test "$scriptFilePath"; then
642         skip_failing_test "$scriptFilePath" "$outputFilePath" &
643     else
644         run_test "$scriptFilePath" "$outputFilePath" &
645     fi
646     processIds[$nextProcessIndex]=$!
647
648     ((nextProcessIndex = (nextProcessIndex + 1) % maxProcesses))
649     ((++processCount))
650 }
651
652 # Get a list of directories in which to scan for tests by reading the
653 # specified file line by line.
654 function set_test_directories {
655     local errorSource='set_test_directories'
656
657     local listFileName=$1
658
659     if [ ! -f "$listFileName" ]
660     then
661         exit_with_error "$errorSource" "Test directories file not found at $listFileName"
662     fi
663     testDirectories=($(read_array "$listFileName"))
664 }
665
666 function run_tests_in_directory {
667     local testDir=$1
668
669     # Recursively search through directories for .sh files to prepare them.
670     for scriptFilePath in $(find "$testDir" -type f -iname '*.sh' | sort)
671     do
672         prep_test "${scriptFilePath:2}"
673     done
674     echo "The tests have been prepared"
675     # Recursively search through directories for .sh files to run.
676     for scriptFilePath in $(find "$testDir" -type f -iname '*.sh' | sort)
677     do
678         start_test "${scriptFilePath:2}"
679     done
680 }
681
682 function coreclr_code_coverage()
683 {
684
685   local coverageDir="$coverageOutputDir/Coverage"
686   local toolsDir="$coverageOutputDir/Coverage/tools"
687   local reportsDir="$coverageOutputDir/Coverage/reports"
688   local packageName="unix-code-coverage-tools.1.0.0.nupkg"
689   rm -rf $coverageDir
690   mkdir -p $coverageDir
691   mkdir -p $toolsDir
692   mkdir -p $reportsDir
693   pushd $toolsDir > /dev/null
694
695   echo "Pulling down code coverage tools"
696   wget -q https://www.myget.org/F/dotnet-buildtools/api/v2/package/unix-code-coverage-tools/1.0.0 -O $packageName
697   echo "Unzipping to $toolsDir"
698   unzip -q -o $packageName
699
700   # Invoke gcovr
701   chmod a+rwx ./gcovr
702   chmod a+rwx ./$OSName/llvm-cov
703
704   echo
705   echo "Generating coreclr code coverage reports at $reportsDir/coreclr.html"
706   echo "./gcovr $coreClrObjs --gcov-executable=$toolsDir/$OS/llvm-cov -r $coreClrSrc --html --html-details -o $reportsDir/coreclr.html"
707   echo
708   ./gcovr $coreClrObjs --gcov-executable=$toolsDir/$OSName/llvm-cov -r $coreClrSrc --html --html-details -o $reportsDir/coreclr.html
709   exitCode=$?
710   popd > /dev/null
711   exit $exitCode
712 }
713
714 # Exit code constants
715 readonly EXIT_CODE_SUCCESS=0       # Script ran normally.
716 readonly EXIT_CODE_EXCEPTION=1     # Script exited because something exceptional happened (e.g. bad arguments, Ctrl-C interrupt).
717 readonly EXIT_CODE_TEST_FAILURE=2  # Script completed successfully, but one or more tests failed.
718
719 # Argument variables
720 testRootDir=
721 testNativeBinDir=
722 coreOverlayDir=
723 coreClrBinDir=
724 mscorlibDir=
725 coreFxBinDir=
726 coreFxNativeBinDir=
727 coreClrObjs=
728 coreClrSrc=
729 coverageOutputDir=
730 testEnv=
731 playlistFile=
732
733 ((disableEventLogging = 0))
734 ((serverGC = 0))
735
736 # Handle arguments
737 verbose=0
738 doCrossgen=0
739
740 for i in "$@"
741 do
742     case $i in
743         -h|--help)
744             print_usage
745             exit $EXIT_CODE_SUCCESS
746             ;;
747         -v|--verbose)
748             verbose=1
749             ;;
750         --crossgen)
751             doCrossgen=1
752             ;;
753         --testRootDir=*)
754             testRootDir=${i#*=}
755             ;;
756         --testNativeBinDir=*)
757             testNativeBinDir=${i#*=}
758             ;;
759         --coreOverlayDir=*)
760             coreOverlayDir=${i#*=}
761             ;;
762         --coreClrBinDir=*)
763             coreClrBinDir=${i#*=}
764             ;;
765         --mscorlibDir=*)
766             mscorlibDir=${i#*=}
767             ;;
768         --coreFxBinDir=*)
769             coreFxBinDir=${i#*=}
770             ;;
771         --coreFxNativeBinDir=*)
772             coreFxNativeBinDir=${i#*=}
773             ;;
774         --testDir=*)
775             testDirectories[${#testDirectories[@]}]=${i#*=}
776             ;;
777         --testDirFile=*)
778             set_test_directories "${i#*=}"
779             ;;
780         --runFailingTestsOnly)
781             ((runFailingTestsOnly = 1))
782             ;;
783         --disableEventLogging)
784             ((disableEventLogging = 1))
785             ;;
786         --sequential)
787             ((maxProcesses = 1))
788             ;;
789         --useServerGC)
790             ((serverGC = 1))
791             ;;
792         --playlist=*)
793             playlistFile=${i#*=}
794             ;;
795         --coreclr-coverage)
796             CoreClrCoverage=ON
797             ;;
798         --coreclr-objs=*)
799             coreClrObjs=${i#*=}
800             ;;
801         --coreclr-src=*)
802             coreClrSrc=${i#*=}
803             ;;
804         --coverage-output-dir=*)
805             coverageOutputDir=${i#*=}
806             ;;
807         --test-env=*)
808             testEnv=${i#*=}
809             ;;            
810         *)
811             echo "Unknown switch: $i"
812             print_usage
813             exit $EXIT_CODE_SUCCESS
814             ;;
815     esac
816 done
817
818 if ((disableEventLogging == 0)); then
819     export COMPlus_EnableEventLog=1
820 fi
821
822 export CORECLR_SERVER_GC="$serverGC"
823
824 if [ -z "$testRootDir" ]; then
825     echo "--testRootDir is required."
826     print_usage
827     exit $EXIT_CODE_EXCEPTION
828 fi
829 if [ ! -d "$testRootDir" ]; then
830     echo "Directory specified by --testRootDir does not exist: $testRootDir"
831     exit $EXIT_CODE_EXCEPTION
832 fi
833
834 # Copy native interop test libraries over to the mscorlib path in
835 # order for interop tests to run on linux.
836 if [ -z "$mscorlibDir" ]; then
837         mscorlibDir=$coreClrBinDir
838 fi
839 if [ -d $mscorlibDir/bin ]; then
840     cp $mscorlibDir/bin/* $mscorlibDir   
841 fi
842
843 # If this is a coverage run, make sure the appropriate args have been passed
844 if [ "$CoreClrCoverage" == "ON" ]
845 then
846     echo "Code coverage is enabled for this run"
847     echo ""
848     if [ ! "$OSName" == "Darwin" ] && [ ! "$OSName" == "Linux" ]
849     then
850         echo "Code Coverage not supported on $OS"
851         exit 1
852     fi
853
854     if [ -z "$coreClrObjs" ]
855     then
856         echo "Coreclr obj files are required to generate code coverage reports"
857         echo "Coreclr obj files root path can be passed using '--coreclr-obj' argument"
858         exit 1
859     fi
860
861     if [ -z "$coreClrSrc" ]
862     then
863         echo "Coreclr src files are required to generate code coverage reports"
864         echo "Coreclr src files root path can be passed using '--coreclr-src' argument"
865         exit 1
866     fi
867
868     if [ -z "$coverageOutputDir" ]
869     then
870         echo "Output directory for coverage results must be specified"
871         echo "Output path can be specified '--coverage-output-dir' argument"
872         exit 1
873     fi
874 fi
875
876 xunit_output_begin
877 create_core_overlay
878 precompile_overlay_assemblies
879 copy_test_native_bin_to_test_root
880
881 if [ -n "$playlistFile" ]
882 then
883     # Use a playlist file exclusively, if it was provided
884     echo "Executing playlist $playlistFile"
885     load_playlist_tests
886 else
887     load_unsupported_tests
888     load_failing_tests
889 fi
890
891 scriptPath=$(dirname $0)
892 ${scriptPath}/setup-runtime-dependencies.sh --outputDir=$coreOverlayDir
893
894 cd "$testRootDir"
895 if [ -z "$testDirectories" ]
896 then
897     # No test directories were specified, so run everything in the current 
898     # directory and its subdirectories.
899     run_tests_in_directory "."
900 else
901     # Otherwise, run all the tests in each specified test directory.
902     for testDir in "${testDirectories[@]}"
903     do
904         if [ ! -d "$testDir" ]; then
905             echo "Test directory does not exist: $testDir"
906         else
907             run_tests_in_directory "./$testDir"
908         fi
909     done
910 fi
911 finish_remaining_tests
912
913 print_results
914 xunit_output_end
915
916 if [ "$CoreClrCoverage" == "ON" ]
917 then
918     coreclr_code_coverage
919 fi
920
921 if ((countFailedTests > 0)); then
922     exit $EXIT_CODE_TEST_FAILURE
923 fi
924
925 exit $EXIT_CODE_SUCCESS