Libraries testing (#178)
authorViktor Hofer <viktor.hofer@microsoft.com>
Thu, 21 Nov 2019 22:13:26 +0000 (23:13 +0100)
committerGitHub <noreply@github.com>
Thu, 21 Nov 2019 22:13:26 +0000 (23:13 +0100)
* Add Libraries Testing framework package as inline

The existing package Microsoft.DotNet.CoreFxTesting lived in Arcade
because of no infrastructure being available to compile local tasks in
the repository. As the runtime repository now offers that we can inline
the testing framework.

* Hardcode configuration for installer.tasks

* Update ReportGenerator global tool version

* Add vstest support

* Update binary serialization blobs

43 files changed:
.config/dotnet-tools.json
.gitignore
Directory.Build.props
NuGet.config
docs/libraries/building/code-coverage.md
docs/libraries/project-docs/developer-guide.md
eng/Build.props
eng/Version.Details.xml
eng/Versions.props
eng/pipelines/libraries/corefx-base.yml
eng/pipelines/libraries/windows.yml
eng/references.targets
eng/testing/RunnerTemplate.Unix.txt [new file with mode: 0644]
eng/testing/RunnerTemplate.Windows.txt [new file with mode: 0644]
eng/testing/coverage.props [new file with mode: 0644]
eng/testing/coverage.targets [new file with mode: 0644]
eng/testing/launchSettings.json [new file with mode: 0644]
eng/testing/launchSettings.targets [new file with mode: 0644]
eng/testing/netfx.exe.config [new file with mode: 0644]
eng/testing/runtimeConfiguration.targets [new file with mode: 0644]
eng/testing/tests.props [new file with mode: 0644]
eng/testing/tests.targets [new file with mode: 0644]
eng/testing/xunit/vstest.props [new file with mode: 0644]
eng/testing/xunit/vstest.targets [new file with mode: 0644]
eng/testing/xunit/xunit.console.props [new file with mode: 0644]
eng/testing/xunit/xunit.console.targets [new file with mode: 0644]
eng/testing/xunit/xunit.props [new file with mode: 0644]
eng/testing/xunit/xunit.runner.json [new file with mode: 0644]
eng/testing/xunit/xunit.targets [new file with mode: 0644]
global.json
src/coreclr/tests/scripts/run-corefx-tests.py
src/libraries/Directory.Build.props
src/libraries/Directory.Build.targets
src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj
src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs
src/libraries/pkg/test/testPackages.proj
src/libraries/pretest.proj
src/libraries/restore/runtime/runtime.depproj
src/libraries/tests.proj
tools-local/tasks/installer.tasks/GenerateFileVersionProps.cs
tools-local/tasks/installer.tasks/GenerateRunScript.cs [new file with mode: 0644]
tools-local/tasks/installer.tasks/GenerateTestSharedFrameworkDepsFile.cs [new file with mode: 0644]
tools-local/tasks/installer.tasks/installer.tasks.csproj

index 8d72881..d18a21c 100644 (file)
@@ -9,7 +9,7 @@
       ]
     },
     "dotnet-reportgenerator-globaltool": {
-      "version": "4.3.0",
+      "version": "4.3.6",
       "commands": [
         "reportgenerator"
       ]
index f39c943..bf23e45 100644 (file)
@@ -50,6 +50,7 @@ cross/android-rootfs/
 #NUNIT
 *.VisualState.xml
 TestResult.xml
+testResults.xml
 
 # Build Results of an ATL Project
 [Dd]ebugPS/
index c902460..d7e46d2 100644 (file)
   <Import Project="Sdk.props" Sdk="Microsoft.DotNet.Arcade.Sdk" Condition="'$(SkipImportArcadeSdkFromRoot)' != 'true'" />
 
   <!-- Common paths -->
-  <PropertyGroup>
-    <!-- Set these properties early enough for libraries as they import the Arcade SDK not early enough.  -->
+
+  <!-- Set these properties early enough for libraries as they import the Arcade SDK not early enough.  -->
+  <PropertyGroup Condition="'$(SkipImportArcadeSdkFromRoot)' == 'true'">
     <RepoRoot>$(MSBuildThisFileDirectory)</RepoRoot>
     <RepositoryEngineeringDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'eng'))</RepositoryEngineeringDir>
+    <ArtifactsDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts'))</ArtifactsDir>
+    <ArtifactsBinDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin'))</ArtifactsBinDir>
+  </PropertyGroup>
 
+  <PropertyGroup>
     <RepoToolsLocalDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'tools-local'))</RepoToolsLocalDir>
     <RepoTasksDir>$([MSBuild]::NormalizeDirectory('$(RepoToolsLocalDir)', 'tasks'))</RepoTasksDir>
 
     <!-- Installer specific, required during restore. -->
-    <InstallerTasksOutputPath>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'installer.tasks', '$(Configuration)'))</InstallerTasksOutputPath>
-    <InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$([MSBuild]::NormalizePath('$(InstallerTasksOutputPath)', 'netstandard2.0', 'installer.tasks.dll'))</InstallerTasksAssemblyPath>
-    <InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' != 'Core'">$([MSBuild]::NormalizePath('$(InstallerTasksOutputPath)', 'net46', 'installer.tasks.dll'))</InstallerTasksAssemblyPath>
+    <InstallerTasksOutputPath>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'installer.tasks'))</InstallerTasksOutputPath>
+    <InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$([MSBuild]::NormalizePath('$(InstallerTasksOutputPath)', 'Debug', 'netstandard2.0', 'installer.tasks.dll'))</InstallerTasksAssemblyPath>
+    <InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' != 'Core'">$([MSBuild]::NormalizePath('$(InstallerTasksOutputPath)', 'Debug', 'net46', 'installer.tasks.dll'))</InstallerTasksAssemblyPath>
     <HostMachineInfoProps>$(ArtifactsObjDir)HostMachineInfo.props</HostMachineInfoProps>
 
     <LibrariesProjectRoot>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'libraries'))</LibrariesProjectRoot>
index 6b6e54b..b656e4d 100644 (file)
@@ -12,6 +12,7 @@
     -->
     <!-- TEST_RESTORE_SOURCES_INSERTION_LINE -->
     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
+    <add key="dotnet-tools" value="https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet-tools/nuget/v3/index.json" />
     <add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
     <add key="dotnet5-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5-transport/nuget/v3/index.json" />
     <add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
index f612695..76e7834 100644 (file)
@@ -45,7 +45,7 @@ The results for this one library will then show up in the aforementioned index.h
 
 And then once the run completes:
 
-    $(TestPath)\report\index.htm
+    $(OutDir)\report\index.htm
 
 **Note:** If you only want to measure the coverage of your local changes (that haven't been pushed to git), run:
 
index 4dd8b73..8865259 100644 (file)
@@ -423,7 +423,7 @@ cd src\System.Collections.Immutable\tests
 dotnet msbuild /t:BuildAndTest /p:Coverage=true
 ```
 
-If coverage succeeds, the individual report can be found at `$(TestPath)\report\index.htm`.
+If coverage succeeds, the individual report can be found at `$(OutDir)\report\index.htm`.
 
 Code coverage reports from the continuous integration system are available from the links on the front page of the corefx repo.
 
index 3f36247..e6bf9aa 100644 (file)
@@ -79,6 +79,7 @@
     </ItemGroup>
 
     <MSBuild Projects="@(RepoTaskProjects)"
+             Properties="Configuration=Debug"
              Targets="Restore;Build"/>
 
     <WriteLinesToFile File="$(RepoTasksOutputFile)"
@@ -89,7 +90,7 @@
   <Target Name="GetRepoTasksSrc">
     <PropertyGroup>
       <RepoTasksDir>$(RepoTasksDir)</RepoTasksDir>
-      <RepoTasksOutputFile>$(ArtifactsObjDir)runtime.tasks\$(Configuration)\build-semaphore.txt</RepoTasksOutputFile>
+      <RepoTasksOutputFile>$(ArtifactsObjDir)runtime.tasks\Debug\build-semaphore.txt</RepoTasksOutputFile>
     </PropertyGroup>
 
     <ItemGroup>
index a0cc682..3e11596 100644 (file)
       <Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
       <Sha>d0bb63d2ec7060714e63ee4082fac48f2e57f3e2</Sha>
     </Dependency>
+    <Dependency Name="Microsoft.NET.Test.Sdk" Version="16.4.0">
+      <Uri>https://github.com/Microsoft/vstest</Uri>
+      <Sha>ca987de449d17284f8c9806df36f42276f13962a</Sha>
+    </Dependency>
   </ToolsetDependencies>
 </Dependencies>
index c388700..25eac8a 100644 (file)
     <RefOnlyNugetPackagingVersion>4.9.4</RefOnlyNugetPackagingVersion>
     <!-- sni -->
     <RuntimeWinX64RuntimeNativeSystemDataSqlClientSniVersion>4.4.0</RuntimeWinX64RuntimeNativeSystemDataSqlClientSniVersion>
-    <RuntimeNativeSystemDataSqlClientSniVersion>4.4.0</RuntimeNativeSystemDataSqlClientSniVersion>
     <!-- Testing -->
-    <MicrosoftNETTestSdkVersion>16.3.0</MicrosoftNETTestSdkVersion>
+    <MicrosoftNETTestSdkVersion>16.4.0</MicrosoftNETTestSdkVersion>
     <XUnitVersion>2.4.1</XUnitVersion>
     <TraceEventVersion>2.0.5</TraceEventVersion>
-    <NewtonsoftJsonVersion>12.0.1</NewtonsoftJsonVersion>
+    <NewtonsoftJsonVersion>12.0.3</NewtonsoftJsonVersion>
+    <XUnitXmlTestLoggerVersion>2.1.26</XUnitXmlTestLoggerVersion>
     <!-- Test data -->
     <SystemIOCompressionTestDataVersion>1.0.16</SystemIOCompressionTestDataVersion>
     <SystemIOPackagingTestDataVersion>1.0.4</SystemIOPackagingTestDataVersion>
index a8ad96c..aac9dbf 100644 (file)
@@ -84,7 +84,7 @@ jobs:
           - _msbuildCommonParameters: /p:OfficialBuildId=$(Build.BuildNumber)
 
         - ${{ if eq(job.submitToHelix, 'true') }}:
-          - _archiveTestsParameter: /p:ArchiveTests=Tests
+          - _archiveTestsParameter: /p:ArchiveTests=true
           - ${{ if eq(parameters.isOfficialBuild, 'true') }}:
             - group: DotNet-HelixApi-Access
 
index 6fd05b3..c1825ea 100644 (file)
@@ -134,7 +134,7 @@ stages:
                       -allconfigurations
                       -arch $(_architecture)
                       /p:RuntimeOS=win10
-                      /p:ArchiveTests=Packages
+                      /p:ArchiveTests=true
                       $(_msbuildCommonParameters)
               displayName: Build Packages and Tests
 
index 3f91056..0c104db 100644 (file)
@@ -17,8 +17,9 @@
     <IncludeDefaultReferences Condition="'$(MSBuildProjectExtension)' == '.csproj'">true</IncludeDefaultReferences>
     <IncludeDefaultReferences Condition="'$(MSBuildProjectExtension)' == '.vbproj'">true</IncludeDefaultReferences>
   </PropertyGroup>
+
   <Target Name="SetupDefaultReferences">
-    <ItemGroup Condition="'$(IncludeDefaultReferences)' =='true'">
+    <ItemGroup Condition="'$(IncludeDefaultReferences)' == 'true'">
       <!-- netstandard is a default reference whenever building for NETStandard or building an implementation assembly -->
       <DefaultReference Condition="('$(TargetsNetStandard)' == 'true' or '$(IsReferenceAssembly)' != 'true') and Exists('$(RefPath)netstandard.dll')" Include="netstandard" />
     </ItemGroup>
     </ItemGroup>
   </Target>
 
+  <Target Name="AddDefaultTestReferences" BeforeTargets="SetupDefaultReferences" Condition="'$(IsTestProject)' == 'true'">
+    <ItemGroup>
+      <DefaultReferenceExclusions Include="@(ReferenceFromRuntime)"/>
+
+      <!-- Reference everything in the targeting pack directory -->
+      <DefaultReferenceDirs Include="$(RefPath)" />
+      <DefaultReferenceItems Include="%(DefaultReferenceDirs.Identity)/*.dll" />
+
+      <DefaultReferenceExclusions>
+        <RefDir>%(DefaultReferenceDirs.Identity)</RefDir>
+      </DefaultReferenceExclusions>
+      <_defaultReferenceExclusionsFullPath Include="%(DefaultReferenceExclusions.RefDir)%(DefaultReferenceExclusions.Identity).dll" />
+
+      <!-- Ensure conflict resolution can see these references. -->
+      <Reference Include="%(DefaultReferenceItems.FullPath)" Private="false" />
+    </ItemGroup>
+  </Target>
+
+  <Target Name="RemoveConflictResolutionAssetsForTests"
+          AfterTargets="_HandlePackageFileConflicts"
+          DependsOnTargets="AddDefaultTestReferences"
+          Condition="'@(_defaultReferenceExclusionsFullPath)' != ''">
+    <ItemGroup>
+      <Reference Remove="@(_defaultReferenceExclusionsFullPath)" />
+    </ItemGroup>
+  </Target>
+
   <Target Name="AddReferencesDynamically" BeforeTargets="PrepareForBuild" />
 </Project>
\ No newline at end of file
diff --git a/eng/testing/RunnerTemplate.Unix.txt b/eng/testing/RunnerTemplate.Unix.txt
new file mode 100644 (file)
index 0000000..80b496e
--- /dev/null
@@ -0,0 +1,200 @@
+#!/usr/bin/env bash
+
+usage()
+{
+  echo "Usage: RunTests.sh {-r|--runtime-path} <runtime-path> [{--rsp-file} <rsp-file>]"
+  echo ""
+  echo "Parameters:"
+  echo "--runtime-path           (Mandatory) Testhost containing the test runtime used during test execution (short: -r)"
+  echo "--rsp-file               RSP file to pass in additional arguments"
+  echo "--help                   Print help and exit (short: -h)"
+}
+
+EXECUTION_DIR=$(dirname "$0")
+RUNTIME_PATH=''
+RSP_FILE=''
+
+while [[ $# > 0 ]]; do
+  opt="$(echo "${1}" | awk '{print tolower($0)}')"
+  case "$opt" in
+    --help|-h)
+      usage
+      exit 0
+      ;;
+    --runtime-path|-r)
+      RUNTIME_PATH=$2
+      shift
+      ;;
+    --rsp-file)
+      RSP_FILE=\@$2
+      shift
+      ;;
+    *)
+      echo "Invalid argument: $1"
+      usage
+      exit -1
+      ;;
+  esac
+
+  shift
+done
+
+if [ "$RUNTIME_PATH" == "" ]; then
+  echo "error: -r|--runtime-path argument is required."
+  usage
+  exit -1
+fi
+
+# Don't use a globally installed SDK.
+export DOTNET_MULTILEVEL_LOOKUP=0
+
+exitcode_list[0]="Exited Successfully"
+exitcode_list[130]="SIGINT  Ctrl-C occurred. Likely tests timed out."
+exitcode_list[131]="SIGQUIT Ctrl-\ occurred. Core dumped."
+exitcode_list[132]="SIGILL  Illegal Instruction. Core dumped. Likely codegen issue."
+exitcode_list[133]="SIGTRAP Breakpoint hit. Core dumped."
+exitcode_list[134]="SIGABRT Abort. Managed or native assert, or runtime check such as heap corruption, caused call to abort(). Core dumped."
+exitcode_list[135]="IGBUS  Unaligned memory access. Core dumped."
+exitcode_list[136]="SIGFPE  Bad floating point arguments. Core dumped."
+exitcode_list[137]="SIGKILL Killed eg by kill"
+exitcode_list[139]="SIGSEGV Illegal memory access. Deref invalid pointer, overrunning buffer, stack overflow etc. Core dumped."
+exitcode_list[143]="SIGTERM Terminated. Usually before SIGKILL."
+exitcode_list[159]="SIGSYS  Bad System Call."
+
+function print_info_from_core_file_using_lldb {
+  local core_file_name=$1
+  local executable_name=$2
+  local plugin_path_name="$RUNTIME_PATH/shared/Microsoft.NETCore.App/9.9.9/libsosplugin.so"
+
+  # check for existence of lldb on the path
+  hash lldb 2>/dev/null || { echo >&2 "lldb was not found. Unable to print core file."; return; }
+
+  # pe, clrstack, and dumpasync are defined in libsosplugin.so
+  if [ ! -f $plugin_path_name ]; then
+    echo $plugin_path_name cannot be found.
+    return
+  fi
+
+  echo ----- start ===============  lldb Output =====================================================
+  echo Printing managed exceptions, managed call stacks, and async state machines.
+  lldb -O "settings set target.exec-search-paths $RUNTIME_PATH" -o "plugin load $plugin_path_name" -o "clrthreads -managedexception" -o "pe -nested" -o "clrstack -all -a -f" -o "dumpasync -fields -stacks -roots" -o "quit"  --core $core_file_name $executable_name
+  echo ----- end ===============  lldb Output =======================================================
+}
+
+function print_info_from_core_file_using_gdb {
+  local core_file_name=$1
+  local executable_name=$2
+
+  # Check for the existence of GDB on the path
+  hash gdb 2>/dev/null || { echo >&2 "GDB was not found. Unable to print core file."; return; }
+
+  echo ----- start ===============  GDB Output =====================================================
+  # Open the dump in GDB and print the stack from each thread. We can add more
+  # commands here if desired.
+  echo printing native stack.
+  gdb --batch -ex "thread apply all bt full" -ex "quit" $executable_name $core_file_name
+  echo ----- end ===============  GDB Output =======================================================
+}
+
+function print_info_from_core_file {
+  local core_file_name=$1
+  local executable_name=$RUNTIME_PATH/$2
+
+  if ! [ -e $executable_name ]; then
+    echo "Unable to find executable $executable_name"
+    return
+  elif ! [ -e $core_file_name ]; then
+    echo "Unable to find core file $core_file_name"
+    return
+  fi
+  echo "Printing info from core file $core_file_name"
+  print_info_from_core_file_using_gdb $core_file_name $executable_name
+  print_info_from_core_file_using_lldb $core_file_name $executable_name
+}
+
+function copy_core_file_to_temp_location {
+  local core_file_name=$1
+
+  local storage_location="/tmp/coredumps"
+
+  # Create the directory (this shouldn't fail even if it already exists).
+  mkdir -p $storage_location
+
+  local new_location=$storage_location/core.$RANDOM
+
+  echo "Copying core file $core_file_name to $new_location in case you need it."
+  cp $core_file_name $new_location
+}
+
+# ========================= BEGIN Core File Setup ============================
+if [ "$(uname -s)" == "Darwin" ]; then
+  # On OS X, we will enable core dump generation only if there are no core 
+  # files already in /cores/ at this point. This is being done to prevent
+  # inadvertently flooding the CI machines with dumps.
+  if [[ ! -d "/cores" || ! "$(ls -A /cores)" ]]; then
+    ulimit -c unlimited
+  fi
+elif [ "$(uname -s)" == "Linux" ]; then
+  # On Linux, we'll enable core file generation unconditionally, and if a dump
+  # is generated, we will print some useful information from it and delete the
+  # dump immediately.
+
+  if [ -e /proc/self/coredump_filter ]; then
+      # Include memory in private and shared file-backed mappings in the dump.
+      # This ensures that we can see disassembly from our shared libraries when
+      # inspecting the contents of the dump. See 'man core' for details.
+      echo -n 0x3F > /proc/self/coredump_filter
+  fi
+
+  ulimit -c unlimited
+fi
+# ========================= END Core File Setup ==============================
+
+# ========================= BEGIN Test Execution =============================
+echo ----- start $(date) ===============  To repro directly: ===================================================== 
+echo pushd $EXECUTION_DIR
+[[RunCommandsEcho]]
+echo popd
+echo ===========================================================================================================
+pushd $EXECUTION_DIR
+[[RunCommands]]
+test_exitcode=$?
+popd
+echo ----- end $(date) ----- exit code $test_exitcode ----------------------------------------------------------
+
+if [ "${exitcode_list[$test_exitcode]}" != "" ]; then
+  echo exit code $test_exitcode means ${exitcode_list[$test_exitcode]}
+fi
+# ========================= END Test Execution ===============================
+
+# ======================= BEGIN Core File Inspection =========================
+pushd $EXECUTION_DIR >/dev/null
+if [[ "$(uname -s)" == "Linux" && $test_exitcode -ne 0 ]]; then
+  echo Looking around for any Linux dump...
+  # Depending on distro/configuration, the core files may either be named "core"
+  # or "core.<PID>" by default. We read /proc/sys/kernel/core_uses_pid to 
+  # determine which it is.
+  core_name_uses_pid=0
+  if [ -e /proc/sys/kernel/core_uses_pid ] && [ "1" == $(cat /proc/sys/kernel/core_uses_pid) ]; then
+    core_name_uses_pid=1
+  fi
+
+  if [ $core_name_uses_pid == "1" ]; then
+    # We don't know what the PID of the process was, so let's look at all core
+    # files whose name matches core.NUMBER
+    echo Looking for files matching core.* ...
+    for f in core.*; do
+      [[ $f =~ core.[0-9]+ ]] && print_info_from_core_file "$f" "dotnet" && copy_core_file_to_temp_location "$f" && rm "$f"
+    done
+  elif [ -f core ]; then
+    echo found a dump named core in $EXECUTION_DIR !
+    print_info_from_core_file "core" "dotnet"
+    copy_core_file_to_temp_location "core"
+    rm "core"
+  else
+    echo ... found no dump in $PWD
+  fi
+fi
+popd >/dev/null
+# ======================== END Core File Inspection ==========================
+exit $test_exitcode
diff --git a/eng/testing/RunnerTemplate.Windows.txt b/eng/testing/RunnerTemplate.Windows.txt
new file mode 100644 (file)
index 0000000..6d956f5
--- /dev/null
@@ -0,0 +1,74 @@
+@echo off
+setlocal enabledelayedexpansion
+
+set EXECUTION_DIR=%~dp0
+
+:argparser_start
+  if "%~1" == "" goto argparser_end
+  set "argparser_currentarg=%~1"
+  shift
+
+  set "argparser_help_specified_inloop="
+  if /i "%argparser_currentarg%"=="-h" ( set "argparser_help_specified_inloop=1" )
+  if /i "%argparser_currentarg%"=="--help" ( set "argparser_help_specified_inloop=1" )
+  if defined argparser_help_specified_inloop (
+    goto usage
+  )
+
+  set "argparser_runtime_path_specified_inloop="
+  if /i "%argparser_currentarg%"=="-r" ( set "argparser_runtime_path_specified_inloop=1" )
+  if /i "%argparser_currentarg%"=="--runtime-path" ( set "argparser_runtime_path_specified_inloop=1" )
+  if defined argparser_runtime_path_specified_inloop (
+    if "%~1" == "" ( goto argparser_invalid )
+    set "RUNTIME_PATH=%~1"
+    goto argparser_break
+  )
+
+  if /i "%argparser_currentarg%"=="--rsp-file" (
+    if "%~1" == "" ( goto argparser_invalid )
+    set "RSP_FILE=@%~1"
+    goto argparser_break
+  )
+
+:argparser_invalid
+  echo Invalid argument or value: %argparser_currentarg%
+  call :usage
+  exit /b -1
+
+:argparser_break
+  shift
+  goto argparser_start
+
+:argparser_end
+
+if not defined RUNTIME_PATH (
+  echo error: -r|--runtime-path argument is required.
+  call :usage
+  exit /b -1
+)
+
+:: Don't use a globally installed SDK.
+set DOTNET_MULTILEVEL_LOOKUP=0
+
+:: ========================= BEGIN Test Execution ============================= 
+echo ----- start %DATE% %TIME% ===============  To repro directly: ===================================================== 
+echo pushd %EXECUTION_DIR%
+[[RunCommandsEcho]]
+echo popd
+echo ===========================================================================================================
+pushd %EXECUTION_DIR%
+@echo on
+[[RunCommands]]
+@echo off
+popd
+echo ----- end %DATE% %TIME% ----- exit code %ERRORLEVEL% ----------------------------------------------------------
+exit /b %ERRORLEVEL%
+:: ========================= END Test Execution =================================
+
+:usage
+echo Usage: RunTests.cmd {-r^|--runtime-path} ^<runtime-path^> [{--rsp-file} ^<rsp-file^>]
+echo.
+echo Parameters:
+echo --runtime-path           (Mandatory) Testhost containing the test runtime used during test execution (short: -r)"
+echo --rsp-file               RSP file to pass in additional arguments
+echo --help                   Print help and exit (short: -h)
diff --git a/eng/testing/coverage.props b/eng/testing/coverage.props
new file mode 100644 (file)
index 0000000..7af3026
--- /dev/null
@@ -0,0 +1,14 @@
+<Project>
+  <PropertyGroup>
+    <CoverageOutputPath Condition="'$(CoverageOutputPath)' == ''">coverage.xml</CoverageOutputPath>
+    <CoverageThreshold Condition="$(CoverageThreshold) == ''">0</CoverageThreshold>
+    <CoverageThresholdType Condition="$(CoverageThresholdType) == ''">line,branch,method</CoverageThresholdType>
+    <CoverageFormat Condition="'$(CoverageFormat)' == ''">opencover</CoverageFormat>
+    <CoverageSourceLink Condition="'$(CoverageSourceLink)' == ''">true</CoverageSourceLink>
+    <CoverageVerbosity Condition="'$(CoverageVerbosity)' == ''">normal</CoverageVerbosity>
+
+    <CoverageReportInputPath Condition="'$(CoverageReportInputPath)' == ''">$(CoverageOutputPath)</CoverageReportInputPath>
+    <CoverageReportTypes Condition="'$(CoverageReportTypes)' == ''">Html</CoverageReportTypes>
+    <CoverageReportVerbosity Condition="'$(CoverageReportVerbosity)' == ''">Info</CoverageReportVerbosity>
+  </PropertyGroup>
+</Project>
diff --git a/eng/testing/coverage.targets b/eng/testing/coverage.targets
new file mode 100644 (file)
index 0000000..c2d9059
--- /dev/null
@@ -0,0 +1,62 @@
+<Project InitialTargets="SetupCoverageFilter">
+  <!-- Wrap RunCommand and RunArguments inside code coverage invocation. -->
+  <PropertyGroup>
+    <RunArguments>"$(TargetFileName)" --target "$(RunCommand)" --targetargs "$(RunArguments)" --format "$(CoverageFormat)" --output "$(CoverageOutputPath)" --threshold "$(CoverageThreshold)" --threshold-type "$(CoverageThresholdType)" --verbosity "$(CoverageVerbosity)"</RunArguments>
+    <RunArguments Condition="'$(CoverageSourceLink)' == 'true'">$(RunArguments) --use-source-link</RunArguments>
+    <RunCommand>"$(DotNetTool)" tool run coverlet</RunCommand>
+  </PropertyGroup>
+
+  <!-- Coverage report -->
+  <PropertyGroup>
+    <CoverageReportDir Condition="'$(CoverageReportDir)' == ''">$([MSBuild]::NormalizeDirectory('$(OutDir)', 'report'))</CoverageReportDir>
+    <CoverageReportResultsPath>$([MSBuild]::NormalizePath('$(CoverageReportDir)', 'index.htm'))</CoverageReportResultsPath>
+    <CoverageReportCommandLine>"$(DotNetTool)" tool run reportgenerator "-reports:$(CoverageReportInputPath)" "-targetdir:$(CoverageReportDir.TrimEnd('\/'))" "-reporttypes:$(CoverageReportTypes)" "-verbosity:$(CoverageReportVerbosity)"</CoverageReportCommandLine>
+  </PropertyGroup>
+
+  <!-- Skip generating individual reports if a full report is generated. -->
+  <ItemGroup Condition="'$(TestAllProjects)' != 'true' and '$(SkipCoverageReport)' != 'true'">
+    <PostRunScriptCommands Include="$(CoverageReportCommandLine)" />
+  </ItemGroup>
+
+  <Target Name="SetupCoverageFilter">
+    <PropertyGroup Condition="'@(CoverageExcludeFile)' != ''">
+      <CoverageExcludeByFileFilter>--exclude-by-file @(CoverageExcludeFile -> '"%(Identity)"', ' --exclude-by-file ')</CoverageExcludeByFileFilter>
+      <RunArguments>$(RunArguments) $(CoverageExcludeByFileFilter)</RunArguments>
+    </PropertyGroup>
+
+    <PropertyGroup Condition="'@(CoverageProbePath)' != ''">
+      <IncludeDirectoriesFilter>--include-directory @(CoverageProbePath -> '"$(RunScriptHostDir)%(Identity)"', ' --include-directory ')</IncludeDirectoriesFilter>
+      <RunArguments>$(RunArguments) $(IncludeDirectoriesFilter)</RunArguments>
+    </PropertyGroup>
+
+    <PropertyGroup Condition="'@(CoverageExclude)' != ''">
+      <CoverageExcludeFilter>--exclude @(CoverageExclude -> '"%(Identity)"', ' --exclude ')</CoverageExcludeFilter>
+      <RunArguments>$(RunArguments) $(CoverageExcludeFilter)</RunArguments>
+    </PropertyGroup>
+
+    <!--
+      We need to filter the data to only the assembly being tested. Otherwise we will gather tons of data about other assemblies.
+      If the code being tested is part of the runtime itself, it requires special treatment.
+    -->
+    <PropertyGroup Condition="'$(AssemblyBeingTested)' == ''">
+      <_ProjectDirectoryUnderSourceDir>$(MSBuildProjectDirectory.SubString($(LibrariesProjectRoot.Length)))</_ProjectDirectoryUnderSourceDir>
+      <AssemblyBeingTested>$(_ProjectDirectoryUnderSourceDir.SubString(0, $(_ProjectDirectoryUnderSourceDir.IndexOfAny("\\/"))))</AssemblyBeingTested>
+    </PropertyGroup>
+
+    <!--
+      By default, code coverage data is only gathered for the assembly being tested.
+      CoverageAssemblies can be passed in to the build to gather coverage on additional assemblies.
+    -->
+    <ItemGroup>
+      <_CoverageAssemblies Include="$(AssemblyBeingTested)" />
+      <_CoverageAssemblies Include="System.Private.CoreLib" Condition="'$(TestRuntime)' == 'true'" />
+      <_CoverageAssemblies Include="@(AssembliesBeingTested)" />
+      <_CoverageAssemblies Include="$(CoverageAssemblies)" Condition="'$(CoverageAssemblies)' != ''" />
+    </ItemGroup>
+
+    <PropertyGroup Condition="'$(CoverageType)' != 'all'">
+      <CoverageFilter>--include @(_CoverageAssemblies -> '"[%(Identity)]*"', ' --include ')</CoverageFilter>
+      <RunArguments>$(RunArguments) $(CoverageFilter)</RunArguments>
+    </PropertyGroup>
+  </Target>
+</Project>
diff --git a/eng/testing/launchSettings.json b/eng/testing/launchSettings.json
new file mode 100644 (file)
index 0000000..c215e10
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "profiles": {
+        ".NET Core xUnit Console": {
+            "commandName": "Executable",
+            "executablePath": "$(TestHostRootPath)dotnet.exe",
+            "commandLineArgs": "$(RunArguments) -parallel none",
+            "workingDirectory": "$(RunWorkingDirectory)"
+        }
+    }
+}
diff --git a/eng/testing/launchSettings.targets b/eng/testing/launchSettings.targets
new file mode 100644 (file)
index 0000000..6caf947
--- /dev/null
@@ -0,0 +1,22 @@
+<Project>
+  <!--
+    Generates the launch settings file in case it doesn't exist yet.
+    This supports scenarios when new projects are added or the file was deleted.
+  -->
+  <PropertyGroup>
+    <LaunchSettingsInputFileFullPath Condition="'$(LaunchSettingsInputFileFullPath)' == ''">$(MSBuildThisFileDirectory)launchSettings.json</LaunchSettingsInputFileFullPath>
+    <LaunchSettingsOutputFileFullPath Condition="'$(LaunchSettingsOutputFileFullPath)' == ''">$([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '$(AppDesignerFolder)', 'launchSettings.json'))</LaunchSettingsOutputFileFullPath>
+    <PrepareForRunDependsOn Condition="!Exists($(LaunchSettingsOutputFileFullPath))">GenerateLaunchSettingsFile;$(PrepareForRunDependsOn);</PrepareForRunDependsOn>
+  </PropertyGroup>
+
+  <!--
+    Target to generate the launch settings file.
+    Either called by PrepareForRunDependsOn if the file doesn't already exist when building
+    the test assembly or invoked manually from the build.proj when doing a full build.
+  -->
+  <Target Name="GenerateLaunchSettingsFile">
+    <Copy SourceFiles="$(LaunchSettingsInputFileFullPath)"
+          DestinationFiles="$(LaunchSettingsOutputFileFullPath)"
+          SkipUnchangedFiles="true" />
+  </Target>
+</Project>
\ No newline at end of file
diff --git a/eng/testing/netfx.exe.config b/eng/testing/netfx.exe.config
new file mode 100644 (file)
index 0000000..ed7d7d0
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <runtime>
+    <developmentMode developerInstallation="true" />
+    <UseRandomizedStringHashAlgorithm enabled="1" />
+  </runtime>
+</configuration>
\ No newline at end of file
diff --git a/eng/testing/runtimeConfiguration.targets b/eng/testing/runtimeConfiguration.targets
new file mode 100644 (file)
index 0000000..6b6c952
--- /dev/null
@@ -0,0 +1,45 @@
+<Project> 
+  <PropertyGroup>
+    <!-- Copies the app.config file to the OutDir. -->
+    <TestRuntimeConfigurationFile Condition="'$(TargetsNetFx)' == 'true'">$(MSBuildThisFileDirectory)netfx.exe.config</TestRuntimeConfigurationFile>
+
+    <!-- By default copy the test runtime config file for executable test projects (+ test support projects). -->
+    <GenerateRuntimeConfigurationFiles Condition="'$(IsTestProject)' == 'true' and ('$(IsTestSupportProject)' != 'true' or '$(OutputType.ToLower())' == 'exe')">true</GenerateRuntimeConfigurationFiles>
+  </PropertyGroup>
+
+  <ItemGroup Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'">
+    <None Include="$(TestRuntimeConfigurationFile)"
+          Condition="Exists('$(TestRuntimeConfigurationFile)')"
+          Link="$(TargetName).exe.config"
+          CopyToOutputDirectory="PreserveNewest"
+          Visible="false" />
+    <!--
+      Include deps.json and runtimeconfig.json in ContentWithTargetPath so they will
+      be copied to the output folder of projects that reference this one.
+      Tracking issue: https://github.com/dotnet/sdk/issues/1675
+    -->
+    <ContentWithTargetPath Include="$(ProjectDepsFilePath)"
+                           Condition="'$(TargetsNetCoreApp)' == 'true' and '$(GenerateDependencyFile)' == 'true'"
+                           CopyToOutputDirectory="PreserveNewest"
+                           TargetPath="$(ProjectDepsFileName)" />
+    <ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)"
+                           Condition="'$(TargetsNetCoreApp)' == 'true'"
+                           CopyToOutputDirectory="PreserveNewest"
+                           TargetPath="$(ProjectRuntimeConfigFileName)" />
+  </ItemGroup>
+
+    <!--
+    We disabled implicit framework references but still want to be treated as framework dependent:
+    1. To have runtimeTargets in the deps file.
+    2. To populate the framework node in the runtimeconfig's runtimeOptions 
+    To do this we manually set the RuntimeFramework.
+    At that point restore and conflict resolution already happened therefore setting the item here has no side effects.
+   -->
+  <Target Name="_SetRuntimeFrameworksForTestAssemblies"
+          Condition="'$(SelfContained)' != 'true'"
+          BeforeTargets="GenerateBuildDependencyFile">
+    <ItemGroup>
+      <RuntimeFramework Include="Microsoft.NETCore.App" Version="$(ProductVersion)" />
+    </ItemGroup>
+  </Target>
+</Project>
\ No newline at end of file
diff --git a/eng/testing/tests.props b/eng/testing/tests.props
new file mode 100644 (file)
index 0000000..1dc35ce
--- /dev/null
@@ -0,0 +1,17 @@
+<Project>
+  <PropertyGroup>
+    <TestProjectName Condition="'$(TestProjectName)' == ''">$(MSBuildProjectName)</TestProjectName>
+    <TestFramework Condition="'$(TestFramework)' == ''">xunit</TestFramework>
+  </PropertyGroup>
+
+  <!-- Set env variable to use the local netfx assemblies instead of the ones in the GAC. -->
+  <ItemGroup Condition="'$(TargetsNetFx)' == 'true'">
+    <RunScriptCommands Include="set DEVPATH=%RUNTIME_PATH%" />
+  </ItemGroup>
+
+  <!--
+    Unit/Functional/Integration test support.
+    Supported runners: xunit.
+  -->
+  <Import Project="$(MSBuildThisFileDirectory)xunit\xunit.props" Condition="'$(TestFramework)' == 'xunit'" />
+</Project>
diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets
new file mode 100644 (file)
index 0000000..527f418
--- /dev/null
@@ -0,0 +1,127 @@
+<Project>
+  <PropertyGroup>
+    <TargetOS Condition="'$(TargetOS)' == ''">$(DefaultOSGroup)</TargetOS>
+    <RunWorkingDirectory>$(OutDir)</RunWorkingDirectory>
+
+    <RunScriptInputName Condition="'$(TargetOS)' == 'Windows_NT'">RunnerTemplate.Windows.txt</RunScriptInputName>
+    <RunScriptInputName Condition="'$(TargetOS)' != 'Windows_NT'">RunnerTemplate.Unix.txt</RunScriptInputName>
+    <RunScriptInputPath>$(MSBuildThisFileDirectory)$(RunScriptInputName)</RunScriptInputPath>
+
+    <RunScriptOutputName Condition="'$(TargetOS)' == 'Windows_NT'">RunTests.cmd</RunScriptOutputName>
+    <RunScriptOutputName Condition="'$(TargetOS)' != 'Windows_NT'">RunTests.sh</RunScriptOutputName>
+    <RunScriptOutputPath>$([MSBuild]::NormalizePath('$(OutDir)', '$(RunScriptOutputName)'))</RunScriptOutputPath>
+
+    <RunScriptHostDir Condition="'$(TargetOS)' == 'Windows_NT'">%RUNTIME_PATH%\</RunScriptHostDir>
+    <RunScriptHostDir Condition="'$(TargetOS)' != 'Windows_NT'">$RUNTIME_PATH/</RunScriptHostDir>
+
+    <RunScriptHost Condition="'$(TargetOS)' == 'Windows_NT'">$(RunScriptHostDir)dotnet.exe</RunScriptHost>
+    <RunScriptHost Condition="'$(TargetOS)' != 'Windows_NT'">$(RunScriptHostDir)dotnet</RunScriptHost>
+  </PropertyGroup>
+
+  <!-- Archive test binaries. -->
+  <Target Name="ArchiveTests"
+          Condition="'$(ArchiveTests)' == 'true'"
+          AfterTargets="PrepareForRun"
+          DependsOnTargets="GenerateRunScript">
+    <Error Condition="'$(TestArchiveTestsDir)' == ''" Text="TestArchiveTestsDir property to archive the test folder must be set." />
+
+    <MakeDir Directories="$(TestArchiveTestsDir)" />
+    <ZipDirectory SourceDirectory="$(OutDir)"
+                  DestinationFile="$([MSBuild]::NormalizePath('$(TestArchiveTestsDir)', '$(TestProjectName).zip'))"
+                  Overwrite="true" />
+  </Target>
+
+  <UsingTask TaskName="GenerateRunScript" AssemblyFile="$(InstallerTasksAssemblyPath)"/>
+  <Target Name="GenerateRunScript">
+    <PropertyGroup>
+      <!-- RSP file support. -->
+      <RunArguments Condition="'$(TargetOS)' == 'Windows_NT'">$(RunArguments) %RSP_FILE%</RunArguments>
+      <RunArguments Condition="'$(TargetOS)' != 'Windows_NT'">$(RunArguments) $RSP_FILE</RunArguments>
+
+      <!-- Escape arguments with user inputs. -->
+      <RunArguments>$([MSBuild]::Escape('$(RunArguments)'))</RunArguments>
+
+      <RunScriptCommand Condition="'$(RunScriptCommand)' == ''">$(RunCommand) $(RunArguments)</RunScriptCommand>
+    </PropertyGroup>
+
+    <!-- Set $(TestDebugger) to eg c:\debuggers\windbg.exe to run tests under a debugger. -->
+    <PropertyGroup Condition="'$(TestDebugger)' != ''">
+      <RunScriptCommand Condition="!$(TestDebugger.Contains('devenv'))">$(TestDebugger) $(RunScriptCommand)</RunScriptCommand>
+      <RunScriptCommand Condition=" $(TestDebugger.Contains('devenv'))">$(TestDebugger) /debugexe $(RunScriptCommand)</RunScriptCommand>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <!--
+        If the PreExecutionTestScript property is set, then it should be set to the full path to a script that will be directly incorporated
+        into the generated runtests script, immediately before the test is run. This can be used to set a number of JIT stress modes,
+        for example. It is intended that this be as late as possible in the generated script, as close as possible to the running of the
+        test. That is why this doesn't appear higher in this file. The idea is that if the included script alters managed code behavior, such as
+        setting various JIT stress modes, we don't want those changes to affect any other managed code invocation (such as test infrastructure
+        written in managed code).
+      -->
+      <RunScriptCommands Condition="'$(PreExecutionTestScript)' != ''" Include="$([System.IO.File]::ReadAllText('$(PreExecutionTestScript)'))" />
+
+      <RunScriptCommands Include="$(RunScriptCommand)" />
+
+      <!-- Do not put anything between this and the GenerateRunScript invocation. -->
+      <RunScriptCommands Include="@(PostRunScriptCommands)" />
+    </ItemGroup>
+
+    <GenerateRunScript RunCommands="@(RunScriptCommands)"
+                       TemplatePath="$(RunScriptInputPath)"
+                       OutputPath="$(RunScriptOutputPath)" />
+
+    <Exec Condition="'$(TargetOS)' != 'Windows_NT'" Command="chmod +x $(RunScriptOutputPath)" />
+  </Target>
+
+  <Target Name="ValidateTestPlatform">
+    <ItemGroup>
+      <UnsupportedPlatformsItems Include="$(UnsupportedPlatforms)" />
+    </ItemGroup>
+
+    <PropertyGroup>
+      <TestDisabled Condition="'%(UnsupportedPlatformsItems.Identity)' == '$(TargetOS)' or '$(ConfigurationErrorMsg)' != ''">true</TestDisabled>
+    </PropertyGroup>
+
+    <Message Text="ValidateTestPlatform found TargetOS of [$(TargetOS)]." Importance="Low" />
+
+    <Message Condition="'%(UnsupportedPlatformsItems.Identity)' == '$(TargetOS)'"
+             Text="Skipping tests in $(AssemblyName) because it is not supported on $(TargetOS)" />
+
+    <Message Condition="'$(ConfigurationErrorMsg)' != ''"
+             Text="Skipping tests in $(AssemblyName) because there is no configuration compatible with the current BuildConfiguration." />
+  </Target>
+
+  <Target Name="RunTests"
+          Condition="'$(TestDisabled)' != 'true'"
+          DependsOnTargets="ValidateTestPlatform">
+    <Error Condition="!Exists('$(TargetPath)')"
+           Text="Test assembly couldn't be found. Make sure to build the test project first." />
+
+    <PropertyGroup>
+      <RunTestsCommand>"$(RunScriptOutputPath)" --runtime-path "$(TestHostRootPath.TrimEnd('\/'))"</RunTestsCommand>
+      <RunTestsCommand Condition="'$(TestRspFile)' != ''">$(RunTestsCommand) --rsp-file "$(TestRspFile)"</RunTestsCommand>
+    </PropertyGroup>
+
+    <!-- Invoke the run script with the test host as the runtime path. -->
+    <Exec Command="$(RunTestsCommand)"
+          ContinueOnError="true"
+          IgnoreStandardErrorWarningFormat="true">
+      <Output PropertyName="TestRunExitCode" TaskParameter="ExitCode" />
+    </Exec>
+
+    <PropertyGroup>
+      <TestRunErrorMessage>One or more tests failed while running tests from '$(TestProjectName)'.</TestRunErrorMessage>
+      <TestRunErrorMessage Condition="Exists('$(TestResultsPath)')">$(TestRunErrorMessage) Please check $(TestResultsPath) for details!</TestRunErrorMessage>
+    </PropertyGroup>
+
+    <Error Condition="'$(TestRunExitCode)' != '0'" Text="$(TestRunErrorMessage)" />
+  </Target>
+
+  <Import Project="$(MSBuildThisFileDirectory)xunit\xunit.targets" Condition="'$(TestFramework)' == 'xunit'" />
+
+  <!-- Main test targets -->
+  <Target Name="Test" DependsOnTargets="GenerateRunScript;RunTests" />
+  <Target Name="BuildAndTest" DependsOnTargets="Build;Test" />
+  <Target Name="RebuildAndTest" DependsOnTargets="Rebuild;Test" />
+</Project>
diff --git a/eng/testing/xunit/vstest.props b/eng/testing/xunit/vstest.props
new file mode 100644 (file)
index 0000000..bcf5476
--- /dev/null
@@ -0,0 +1,20 @@
+<Project>
+  <PropertyGroup>
+    <!-- Microsoft.Net.Test.Sdk brings a lot of satellite assemblies in. -->
+    <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="xunit.runner.visualstudio" Version="$(XUnitVersion)" />
+    <PackageReference Include="XunitXml.TestLogger" Version="$(XUnitXmlTestLoggerVersion)" />
+
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
+    <PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Include="$(TestRunnerConfigPath)"
+          CopyToOutputDirectory="PreserveNewest"
+          Visible="false" />
+  </ItemGroup>
+</Project>
diff --git a/eng/testing/xunit/vstest.targets b/eng/testing/xunit/vstest.targets
new file mode 100644 (file)
index 0000000..9e1517a
--- /dev/null
@@ -0,0 +1,32 @@
+<Project>
+  <PropertyGroup>
+    <RunCommand>"$(RunScriptHost)"</RunCommand>
+    <RunArguments>test $(TargetFileName)</RunArguments>
+
+    <!-- CLI options -->
+    <RunArguments>$(RunArguments) --logger "xunit;LogFilePath=$(TestResultsName)"</RunArguments>
+    <RunArguments>$(RunArguments) --framework $(TargetFramework)</RunArguments>
+    <RunArguments>$(RunArguments) --platform $(ArchGroup)</RunArguments>
+    <RunArguments Condition="'$(TestDisableParallelization)' != 'true'">$(RunArguments) --parallel</RunArguments>
+    <RunArguments Condition="'$(TestBlame)' == 'true'">$(RunArguments) --blame</RunArguments>
+
+    <!-- Categories -->
+    <_testFilter Condition="'$(_withCategories)' != ''">$(_withCategories.Replace(';', '&amp;category='))</_testFilter>
+    <_testFilter Condition="'$(_withoutCategories)' != ''">$(_testFilter)$(_withoutCategories.Replace(';', '&amp;category!='))</_testFilter>
+    <!-- On Windows in bat scripts with delayed expansion enabled we need to escape the bang operator. -->
+    <_testFilter Condition="'$(TargetOS)' == 'Windows_NT'">$(_testFilter.Replace('!=', '^!='))</_testFilter>
+    <_testFilter>$(_testFilter.Trim('&amp;'))</_testFilter>
+    <!-- On Windows in bat scripts with delayed expansion enabled we need to escape the bang operator. -->
+    <_testFilter Condition="'$(TestFilter)' != ''">$(_testFilter)&amp;$(TestFilter.Replace('!=', '^!='))</_testFilter>
+
+    <RunArguments>$(RunArguments) --filter "($(_testFilter))"</RunArguments>
+
+    <!-- User passed in options. -->
+    <RunArguments Condition="'$(XUnitOptions)' != ''">$(RunArguments) $(XUnitOptions)</RunArguments>
+
+    <!-- RunConfiguration settings. -->
+    <RunSettingsOptions Condition="'$(TestDisableParallelization)' == 'true'">$(RunSettingsOptions) RunConfiguration.DisableParallelization=true</RunSettingsOptions>
+    <RunSettingsOptions Condition="'$(TestDisableAppDomain)' == 'true'">$(RunSettingsOptions) RunConfiguration.DisableAppDomain=true</RunSettingsOptions>
+    <RunArguments Condition="'$(RunSettingsOptions)' != ''">$(RunArguments)  --(RunSettingsOptions)</RunArguments>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/eng/testing/xunit/xunit.console.props b/eng/testing/xunit/xunit.console.props
new file mode 100644 (file)
index 0000000..48a3919
--- /dev/null
@@ -0,0 +1,21 @@
+<Project>
+  <PropertyGroup>
+    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Condition="'$(TargetsNetCoreApp)' == 'true'" Include="Microsoft.DotNet.XUnitConsoleRunner" Version="$(MicrosoftDotNetXUnitConsoleRunnerVersion)" />
+    <PackageReference Condition="'$(TargetsNetFx)' == 'true'" Include="xunit.runner.console" Version="$(XUnitVersion)" />
+
+    <!-- Microsoft.Net.Test.Sdk brings a lot of assemblies with it. To reduce helix payload submission size we disable it on CI. -->
+    <PackageReference Condition="'$(ArchiveTest)' != 'true'" Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
+    <PackageReference Condition="'$(ArchiveTest)' != 'true'" Include="xunit.runner.visualstudio" Version="$(XUnitVersion)" />
+    <!--
+      Microsoft.Net.Test.Sdk has a dependency on Newtonsoft.Json v9.0.1. We upgrade the dependency version
+      with the one used in corefx to have a consistent set of dependency versions. Additionally this works
+      around a dupliate type between System.Runtime.Serialization.Formatters and Newtonsoft.Json.
+    -->
+    <PackageReference Condition="'$(ArchiveTest)' != 'true'" Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
+
+  </ItemGroup>
+</Project>
diff --git a/eng/testing/xunit/xunit.console.targets b/eng/testing/xunit/xunit.console.targets
new file mode 100644 (file)
index 0000000..c7f10a9
--- /dev/null
@@ -0,0 +1,75 @@
+<Project>
+  <PropertyGroup>
+    <RunArguments>$(TargetFileName)</RunArguments>
+    <RunArguments>$(RunArguments) -xml $(TestResultsName)</RunArguments>
+    <RunArguments>$(RunArguments) -nologo</RunArguments>
+    <RunArguments Condition="'$(ArchiveTest)' == 'true'">$(RunArguments) -nocolor</RunArguments>
+
+    <!-- Add local and global options to the argument stack. -->
+    <RunArguments Condition="'$(TestDisableParallelization)' == 'true'">$(RunArguments) -maxthreads 1</RunArguments>
+    <RunArguments Condition="'$(XUnitMethodName)' != ''">$(RunArguments) -method $(XUnitMethodName)</RunArguments>
+    <RunArguments Condition="'$(XUnitClassName)' != ''">$(RunArguments) -class $(XUnitClassName)</RunArguments>
+    <RunArguments Condition="'$(XUnitShowProgress)' == 'true'">$(RunArguments) -verbose</RunArguments>
+    <RunArguments Condition="'$(TargetsNetFx)' == 'true' and '$(TestDisableAppDomain)' == 'true'">$(RunArguments) -noappdomain</RunArguments>
+
+    <!-- Add to run argument string -->
+    <RunArguments>$(RunArguments)$(_withCategories.Replace(';', ' -trait category='))</RunArguments>
+    <RunArguments>$(RunArguments)$(_withoutCategories.Replace(';', ' -notrait category='))</RunArguments>
+
+    <!-- User passed in options. -->
+    <RunArguments Condition="'$(XUnitOptions)' != ''">$(RunArguments) $(XUnitOptions)</RunArguments>
+  </PropertyGroup>
+
+  <!-- Overwrite the runner config file with the app local one. -->
+  <Target Name="OverwriteDesktopTestRunnerConfigs"
+          Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true' and '$(TargetsNetFx)' == 'true'"
+          AfterTargets="CopyFilesToOutputDirectory">
+
+    <ItemGroup>
+      <_testRunnerConfigSourceFile Include="$(TargetDir)$(TargetName).exe.config" />
+      <_testRunnerConfigDestFile Include="$(TargetDir)$(_testRunnerName).config" />
+    </ItemGroup>
+
+    <Copy SourceFiles="@(_testRunnerConfigSourceFile)"
+          Condition="'@(_testRunnerConfigSourceFile)' != ''"
+          DestinationFiles="@(_testRunnerConfigDestFile)"
+          SkipUnchangedFiles="true" />
+
+  </Target>
+
+  <!-- Setup run commands. -->
+  <PropertyGroup Condition="'$(TargetsNetCoreApp)' == 'true'">
+    <RunCommand>"$(RunScriptHost)"</RunCommand>
+    <_depsFileRunArgument Condition="'$(GenerateDependencyFile)' == 'true'">--depsfile $(AssemblyName).deps.json</_depsFileRunArgument>
+    <RunArguments>exec --runtimeconfig $(AssemblyName).runtimeconfig.json $(_depsFileRunArgument) xunit.console.dll $(RunArguments)</RunArguments>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(TargetsNetFx)' == 'true'">
+    <_testRunnerName>xunit.console.exe</_testRunnerName>
+    <RunCommand>$(_testRunnerName)</RunCommand>
+  </PropertyGroup>
+
+  <!-- ResolveAssemblyReferences is the target that populates ReferenceCopyLocalPaths which is what is copied to output directory. -->
+  <Target Name="CopyRunnerToOutputDirectory" BeforeTargets="ResolveAssemblyReferences">
+    <ItemGroup>
+      <!-- Add the runner configuration file -->
+      <None Include="$(TestRunnerConfigPath)"
+            CopyToOutputDirectory="PreserveNewest"
+            Visible="false" />
+
+      <!-- Copy test runner to output directory -->
+      <None Include="$([System.IO.Path]::GetDirectoryName('$(XunitConsole472Path)'))\*"
+            Exclude="$([System.IO.Path]::GetDirectoryName('$(XunitConsole472Path)'))\xunit.console.*exe.config;$([System.IO.Path]::GetDirectoryName('$(XunitConsole472Path)'))\xunit.console.x86.exe"
+            Condition="'$(TargetsNetFx)' == 'true' and '$(XunitConsole472Path)' != ''"
+            CopyToOutputDirectory="PreserveNewest"
+            Visible="false" />
+
+      <_xunitConsoleNetCoreExclude Condition="'$(GenerateDependencyFile)' != 'true'" Include="$([System.IO.Path]::GetDirectoryName('$(XunitConsoleNetCore21AppPath)'))\xunit.console.deps.json" />
+      <None Include="$([System.IO.Path]::GetDirectoryName('$(XunitConsoleNetCore21AppPath)'))\*"
+            Exclude="@(_xunitConsoleNetCoreExclude)"
+            Condition="'$(TargetsNetCoreApp)' == 'true' and '$(XunitConsoleNetCore21AppPath)' != ''"
+            CopyToOutputDirectory="PreserveNewest"
+            Visible="false" />
+    </ItemGroup>
+  </Target>
+</Project>
diff --git a/eng/testing/xunit/xunit.props b/eng/testing/xunit/xunit.props
new file mode 100644 (file)
index 0000000..2282ba7
--- /dev/null
@@ -0,0 +1,18 @@
+<Project>
+  <PropertyGroup>
+    <TestRunner Condition="'$(TestRunner)' == ''">xunit.console</TestRunner>
+    <TestRunnerConfigPath>$(MSBuildThisFileDirectory)xunit.runner.json</TestRunnerConfigPath>
+    <TestResultsName>testResults.xml</TestResultsName>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Condition="'$(IncludeRemoteExecutor)' == 'true'" Include="Microsoft.DotNet.RemoteExecutor" Version="$(MicrosoftDotNetRemoteExecutorVersion)" />
+
+    <!-- Excluding xunit.core/build as it enables deps file generation. -->
+    <PackageReference Include="xunit" Version="$(XUnitVersion)" ExcludeAssets="build" />
+    <PackageReference Include="Microsoft.DotNet.XUnitExtensions" Version="$(MicrosoftDotNetXUnitExtensionsVersion)" />
+  </ItemGroup>
+
+  <Import Project="$(MSBuildThisFileDirectory)xunit.console.props" Condition="'$(TestRunner)' == 'xunit.console'" />
+  <Import Project="$(MSBuildThisFileDirectory)vstest.props" Condition="'$(TestRunner)' == 'vstest'" />
+</Project>
diff --git a/eng/testing/xunit/xunit.runner.json b/eng/testing/xunit/xunit.runner.json
new file mode 100644 (file)
index 0000000..5d2ac04
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "diagnosticMessages": true,
+    "longRunningTestSeconds": 120,
+    "shadowCopy": false
+}
\ No newline at end of file
diff --git a/eng/testing/xunit/xunit.targets b/eng/testing/xunit/xunit.targets
new file mode 100644 (file)
index 0000000..4ddddcc
--- /dev/null
@@ -0,0 +1,31 @@
+<Project>
+  <PropertyGroup>
+    <TargetOSCategory Condition="'$(TargetOS)' == 'Windows_NT'">nonwindowstests</TargetOSCategory>
+    <TargetOSCategory Condition="'$(TargetOS)' == 'Linux'">nonlinuxtests</TargetOSCategory>
+    <TargetOSCategory Condition="'$(TargetOS)' == 'OSX'">nonosxtests</TargetOSCategory>
+    <TargetOSCategory Condition="'$(TargetOS)' == 'FreeBSD'">nonfreebsdtests</TargetOSCategory>
+    <TargetOSCategory Condition="'$(TargetOS)' == 'NetBSD'">nonnetbsdtests</TargetOSCategory>
+
+    <!-- Default and user defined categories -->
+    <_withCategories Condition="'$(WithCategories)' != ''">;$(WithCategories.Trim(';'))</_withCategories>
+    <_withoutCategories Condition="'$(WithoutCategories)' != ''">;$(WithoutCategories.Trim(';'))</_withoutCategories>
+
+    <TestScope Condition="'$(TestScope)' == '' and '$(Outerloop)' == 'true'">all</TestScope>
+    <_withCategories Condition="'$(TestScope)' == 'outerloop'">$(_withCategories);OuterLoop</_withCategories>
+    <_withoutCategories Condition="'$(ArchiveTest)' == 'true'">$(_withoutCategories);IgnoreForCI</_withoutCategories>
+    <_withoutCategories Condition="'$(TestScope)' == '' or '$(TestScope)' == 'innerloop'">$(_withoutCategories);OuterLoop</_withoutCategories>
+    <_withoutCategories Condition="!$(_withCategories.Contains('failing'))">$(_withoutCategories);failing</_withoutCategories>
+
+    <_withoutCategories>$(_withoutCategories);non$(_bc_TargetGroup)tests</_withoutCategories>
+    <_withoutCategories Condition="'$(TargetOSCategory)' != ''">$(_withoutCategories);$(TargetOSCategory)</_withoutCategories>
+  </PropertyGroup>
+
+  <Target Name="ValidateTargetOSCategory"
+          BeforeTargets="GenerateRunScript">
+    <Error Condition="'$(TargetOSCategory)' == ''"
+           Text="TargetOS [$(TargetOS)] is unknown so we don't know how to configure the test run for this project [$(TestProjectName)]" />
+  </Target>
+
+  <Import Project="$(MSBuildThisFileDirectory)xunit.console.targets" Condition="'$(TestRunner)' == 'xunit.console'" />
+  <Import Project="$(MSBuildThisFileDirectory)vstest.targets" Condition="'$(TestRunner)' == 'vstest'" />
+</Project>
index 5cf141c..bf792d3 100644 (file)
@@ -16,7 +16,6 @@
     "Microsoft.DotNet.Build.Tasks.SharedFramework.Sdk": "5.0.0-beta.19567.2",
     "Microsoft.DotNet.Helix.Sdk": "5.0.0-beta.19567.2",
     "Microsoft.DotNet.Build.Tasks.Configuration": "5.0.0-beta.19567.2",
-    "Microsoft.DotNet.CoreFxTesting": "5.0.0-beta.19567.2",
     "FIX-85B6-MERGE-9C38-CONFLICT": "1.0.0",
     "Microsoft.NET.Sdk.IL": "5.0.0-alpha1.19563.3",
     "Microsoft.Build.NoTargets": "1.0.53",
index b346097..1dad960 100644 (file)
@@ -300,7 +300,7 @@ def main(args):
 
     common_config_args = '-configuration Release -framework netcoreapp -os %s -arch %s' % (clr_os, arch)
     build_args = '-build -restore'
-    build_test_args = '-buildtests  /p:ArchiveTests=Tests'
+    build_test_args = '-buildtests  /p:ArchiveTests=true'
 
     if not no_run_tests:
         build_test_args += ' -test'
index 84d0266..dd2bcf6 100644 (file)
   </PropertyGroup>
 
   <Import Sdk="Microsoft.DotNet.Build.Tasks.Configuration" Project="Sdk.props" />
-  <Import Sdk="Microsoft.DotNet.CoreFxTesting" Project="Sdk.props" Condition="'$(DotNetBuildFromSource)' != 'true' and '$(BuildAllConfigurations)' != 'true'" />
+
+  <!-- Define test projects and companions -->
+  <PropertyGroup>
+    <IsTestProject>false</IsTestProject>
+    <IsTestProject Condition="$(MSBuildProjectName.EndsWith('.UnitTests')) or $(MSBuildProjectName.EndsWith('.Tests'))">true</IsTestProject>
+
+    <IsTestSupportProject>false</IsTestSupportProject>
+    <IsTestSupportProject Condition="($(MSBuildProjectFullPath.Contains('\tests\')) OR $(MSBuildProjectFullPath.Contains('/tests/'))) AND '$(IsTestProject)' != 'true'">true</IsTestSupportProject>
+    <IsTestProject Condition="'$(IsTestSupportProject)' == 'true'">true</IsTestProject>
+
+    <!-- Treat test assemblies as non-shipping (do not publish or sign them). -->
+    <IsShipping Condition="'$(IsTestProject)' == 'true'">false</IsShipping>
+  </PropertyGroup>
 
   <PropertyGroup>    
     <EnableProjectRestore Condition="'$(IsTestProject)' == 'true'">true</EnableProjectRestore>
     <VersionFileForPackages Condition="'$(VersionFileForPackages)' == ''">$(ArtifactsObjDir)version.txt</VersionFileForPackages>
   </PropertyGroup>
 
-  <PropertyGroup Condition="'$(BuildAllConfigurations)' != 'true'">
-    <!-- We add extra binplacing for the test shared framework until we can get hardlinking with the runtime directory working on all platforms -->
-    <BinPlaceTestSharedFramework Condition="'$(_bc_TargetGroup)' == 'netcoreapp'">true</BinPlaceTestSharedFramework>
-    <BinPlaceNETFXRuntime Condition="'$(_bc_TargetGroup)' == 'netfx'">true</BinPlaceNETFXRuntime>
-
-    <NETCoreAppTestSharedFrameworkPath>$([MSBuild]::NormalizeDirectory('$(TestHostRootPath)', 'shared', 'Microsoft.NETCore.App', '$(ProductVersion)'))</NETCoreAppTestSharedFrameworkPath>
-
-    <TestHostRuntimePath Condition="'$(BinPlaceTestSharedFramework)' == 'true'">$(NETCoreAppTestSharedFrameworkPath)</TestHostRuntimePath>
-    <TestHostRuntimePath Condition="'$(BinPlaceNETFXRuntime)' == 'true'">$(TestHostRootPath)</TestHostRuntimePath>
-
-    <PlatformManifestFile>$(TestHostRuntimePath)PlatformManifest.txt</PlatformManifestFile>
-  </PropertyGroup>
-
-  <ItemGroup Condition="'$(PlatformManifestFile)' != '' and '$(IsTestProject)' == 'true'">
-    <PackageConflictPlatformManifests Include="$(PlatformManifestFile)" />
-  </ItemGroup>
-
-  <PropertyGroup>
-    <!-- F5 and debugging support for netcoreapp and netfx inside VS. -->
-    <EnableLaunchSettings Condition="'$(EnableLaunchSettings)' == '' and '$(DotNetBuildFromSource)' != 'true'">true</EnableLaunchSettings>
-  </PropertyGroup>
-
-  <!-- Test properties for full build. -->
-  <PropertyGroup Condition="'$(TestAllProjects)' == 'true'">
-    <GenerateFullCoverageReport Condition="'$(GenerateFullCoverageReport)' == ''">true</GenerateFullCoverageReport>
-  </PropertyGroup>
-
   <Import Project="$(RepositoryEngineeringDir)references.props" />
 
   <!-- Import it at the end of the props file to override the OutputPath for reference assemblies and use common directory props -->
   <Import Project="$(RepositoryEngineeringDir)referenceAssemblies.props" />
 
-  <PropertyGroup Condition="'$(IsSourceProject)' == 'true'">
+  <PropertyGroup>
+    <EnableDefaultItems>false</EnableDefaultItems>
+    <EmbedUntrackedSources>true</EmbedUntrackedSources>
+    <DisableImplicitConfigurationDefines>true</DisableImplicitConfigurationDefines>
+
+    <!-- Workaround for https://github.com/microsoft/msbuild/issues/4474 -->
+    <GenerateResourceUsePreserializedResources>false</GenerateResourceUsePreserializedResources>
+
     <!-- Set the documentation output file globally. -->
-    <DocumentationFile Condition="'$(DocumentationFile)' == ''">$(OutputPath)$(MSBuildProjectName).xml</DocumentationFile>
-  </PropertyGroup>
+    <DocumentationFile Condition="'$(IsSourceProject)' == 'true' and '$(DocumentationFile)' == ''">$(OutputPath)$(MSBuildProjectName).xml</DocumentationFile>
+
+    <CodeAnalysisRuleset>$(LibrariesProjectRoot)CodeAnalysis.ruleset</CodeAnalysisRuleset>
+    <EnablePinvokeUWPAnalyzer>false</EnablePinvokeUWPAnalyzer>
 
-  <!-- Additional optimizations -->
-  <PropertyGroup>
     <!-- Clear the init locals flag on all src projects, except those in VB, where we can't use spans. -->
     <ILLinkClearInitLocals Condition="'$(IsSourceProject)' == 'true' and '$(Language)' != 'VB'">true</ILLinkClearInitLocals>
   </PropertyGroup>
     <IsShippingPackage Condition="$(MSBuildProjectName.Contains('Private')) and '$(MSBuildProjectExtension)' == '.pkgproj'">false</IsShippingPackage>
   </PropertyGroup>
 
-  <PropertyGroup>
-    <EnableDefaultItems>false</EnableDefaultItems>
-    <EmbedUntrackedSources>true</EmbedUntrackedSources>
-    <DisableImplicitConfigurationDefines>true</DisableImplicitConfigurationDefines>
+  <PropertyGroup Condition="'$(BuildAllConfigurations)' != 'true'">
+    <!-- We add extra binplacing for the test shared framework until we can get hardlinking with the runtime directory working on all platforms -->
+    <BinPlaceTestSharedFramework Condition="'$(_bc_TargetGroup)' == 'netcoreapp'">true</BinPlaceTestSharedFramework>
+    <BinPlaceNETFXRuntime Condition="'$(_bc_TargetGroup)' == 'netfx'">true</BinPlaceNETFXRuntime>
 
-    <!-- Workaround for https://github.com/microsoft/msbuild/issues/4474 -->
-    <GenerateResourceUsePreserializedResources>false</GenerateResourceUsePreserializedResources>
+    <NETCoreAppTestSharedFrameworkPath>$([MSBuild]::NormalizeDirectory('$(TestHostRootPath)', 'shared', 'Microsoft.NETCore.App', '$(ProductVersion)'))</NETCoreAppTestSharedFrameworkPath>
 
-    <CodeAnalysisRuleset>$(LibrariesProjectRoot)CodeAnalysis.ruleset</CodeAnalysisRuleset>
-    <EnablePinvokeUWPAnalyzer>false</EnablePinvokeUWPAnalyzer>
+    <TestHostRuntimePath Condition="'$(BinPlaceTestSharedFramework)' == 'true'">$(NETCoreAppTestSharedFrameworkPath)</TestHostRuntimePath>
+    <TestHostRuntimePath Condition="'$(BinPlaceNETFXRuntime)' == 'true'">$(TestHostRootPath)</TestHostRuntimePath>
+
+    <PlatformManifestFile Condition="'$(BinPlaceTestSharedFramework)' == 'true'">$(TestHostRuntimePath)PlatformManifest.txt</PlatformManifestFile>
   </PropertyGroup>
 
+  <ItemGroup Condition="'$(PlatformManifestFile)' != '' and '$(IsTestProject)' == 'true'">
+    <PackageConflictPlatformManifests Include="$(PlatformManifestFile)" />
+  </ItemGroup>
+
   <ItemGroup Condition="'$(IsTestProject)' == 'true' and '$(IsTestSupportProject)' != 'true'">
-    <ProjectReference Include="$(CommonTestPath)\CoreFx.Private.TestUtilities\CoreFx.Private.TestUtilities.csproj" />
+    <ProjectReference Include="$(CommonTestPath)CoreFx.Private.TestUtilities\CoreFx.Private.TestUtilities.csproj" />
   </ItemGroup>
 
+  <PropertyGroup Condition="'$(IsTestProject)' == 'true' and '$(IsTestSupportProject)' != 'true'">
+    <EnableTestSupport>true</EnableTestSupport>
+    <EnableCoverageSupport Condition="'$(Coverage)' == 'true' and '$(CoverageSupported)' != 'false'">true</EnableCoverageSupport>
+    <EnableLaunchSettings Condition="'$(DotNetBuildFromSource)' != 'true'">true</EnableLaunchSettings>
+  </PropertyGroup>
+
+  <Import Project="$(RepositoryEngineeringDir)testing\tests.props" Condition="'$(EnableTestSupport)' == 'true'" />
+  <Import Project="$(RepositoryEngineeringDir)testing\coverage.props" Condition="'$(EnableCoverageSupport)' == 'true'" />
+
   <!-- Use msbuild path functions as that property is used in bash scripts. -->
   <ItemGroup>
     <CoverageExcludeFile Include="$([MSBuild]::NormalizePath('$(LibrariesProjectRoot)', 'Common', 'src', 'System', 'SR.*'))" />
index 4178908..0842649 100644 (file)
   <Import Project="$(RepositoryEngineeringDir)depProj.targets" Condition="'$(MSBuildProjectExtension)' == '.depproj'" />
   <Import Project="$(RepositoryEngineeringDir)references.targets" />
   <Import Project="$(RepositoryEngineeringDir)resolveContract.targets" />
+  <Import Project="$(RepositoryEngineeringDir)testing\runtimeConfiguration.targets" />
+  <Import Project="$(RepositoryEngineeringDir)testing\launchSettings.targets" Condition="'$(EnableLaunchSettings)' == 'true'" />
+  <Import Project="$(RepositoryEngineeringDir)testing\tests.targets" Condition="'$(EnableTestSupport)' == 'true'" />
+  <Import Project="$(RepositoryEngineeringDir)testing\coverage.targets" Condition="'$(EnableCoverageSupport)' == 'true'" />
 
   <Import Sdk="Microsoft.DotNet.Build.Tasks.Configuration" Project="Sdk.targets" />
-  <Import Sdk="Microsoft.DotNet.CoreFxTesting" Project="Sdk.targets" Condition="'$(DotNetBuildFromSource)' != 'true' and '$(BuildAllConfigurations)' != 'true'" />
   <Import Condition="'$(EnableProjectRestore)' != 'true'" Project="$(RepositoryEngineeringDir)restore\repoRestore.targets" />
 
   <Import Project="$(RepositoryEngineeringDir)referenceFromRuntime.targets" />
     </ItemGroup>
   </Target>
 
-  <!-- Routing dotnet test to a test project's BuildAndTest target. -->
-  <Target Name="VSTest"
-          Condition="'$(IsTestProject)' == 'true' and '$(IsTestSupportProject)' != 'true'"
-          DependsOnTargets="BuildAndTest" />
-
-  <Target Name="ValidateTestProjectConfigurations"
-          BeforeTargets="CoreCompile"
-          Condition="'$(IsTestProject)' == 'true' and '$(IsTestSupportProject)' != 'true' and '$(TargetsNetStandard)' == 'true'">
-    <Error Text="Targeting .NET Standard in a test project isn't supported." />
-  </Target>
-
   <PropertyGroup>
     <!--
     Hack workaround to skip the GenerateCompiledExpressionsTempFile target in
index 2a888c9..f0efd58 100644 (file)
@@ -1,12 +1,11 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <!-- XUnit AppDomains BaseDirectory path doesn't contain a trailing slash, which impacts our tests. -->
-    <XUnitNoAppdomain>true</XUnitNoAppdomain>
-    <XUnitMaxThreads>1</XUnitMaxThreads>
+    <TestDisableAppDomain>true</TestDisableAppDomain>
+    <TestDisableParallelization>true</TestDisableParallelization>
     <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
     <Configurations>netcoreapp-Debug;netcoreapp-Release;netfx-Debug;netfx-Release</Configurations>
   </PropertyGroup>
-  <!-- Default configurations to help VS understand the options -->
   <ItemGroup>
     <Compile Include="$(CommonTestPath)System\IO\TempDirectory.cs">
       <Link>Common\System\IO\TempDirectory.cs</Link>
index 5232c1a..31ecb26 100644 (file)
@@ -159,7 +159,7 @@ namespace System.Runtime.Serialization.Formatters.Tests
             // To generate this properly, change AssemblyVersion to a value which is unlikely to happen in production and generate base64(serialized-data)
             // For this test 9.98.7.987 is being used
             var obj = new SomeType() { SomeField = 7 };
-            string serializedObj = @"AAEAAAD/////AQAAAAAAAAAMAgAAAHNTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249OS45OC43Ljk4NywgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05ZDc3Y2M3YWQzOWI2OGViBQEAAAA2U3lzdGVtLlJ1bnRpbWUuU2VyaWFsaXphdGlvbi5Gb3JtYXR0ZXJzLlRlc3RzLlNvbWVUeXBlAQAAAAlTb21lRmllbGQACAIAAAAHAAAACw==";
+            string serializedObj = @"AAEAAAD/////AQAAAAAAAAAMAgAAAHBTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249NS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1jYzdiMTNmZmNkMmRkZDUxBQEAAAA2U3lzdGVtLlJ1bnRpbWUuU2VyaWFsaXphdGlvbi5Gb3JtYXR0ZXJzLlRlc3RzLlNvbWVUeXBlAQAAAAlTb21lRmllbGQACAIAAAAHAAAACw==";
 
             var deserialized = (SomeType)BinaryFormatterHelpers.FromBase64String(serializedObj, FormatterAssemblyStyle.Simple);
             Assert.Equal(obj, deserialized);
@@ -171,7 +171,7 @@ namespace System.Runtime.Serialization.Formatters.Tests
             // To generate this properly, change AssemblyVersion to a value which is unlikely to happen in production and generate base64(serialized-data)
             // For this test 9.98.7.987 is being used
             var obj = new GenericTypeWithArg<SomeType>() { Test = new SomeType() { SomeField = 9 } };
-            string serializedObj = @"AAEAAAD/////AQAAAAAAAAAMAgAAAHNTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249OS45OC43Ljk4NywgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05ZDc3Y2M3YWQzOWI2OGViBQEAAADxAVN5c3RlbS5SdW50aW1lLlNlcmlhbGl6YXRpb24uRm9ybWF0dGVycy5UZXN0cy5HZW5lcmljVHlwZVdpdGhBcmdgMVtbU3lzdGVtLlJ1bnRpbWUuU2VyaWFsaXphdGlvbi5Gb3JtYXR0ZXJzLlRlc3RzLlNvbWVUeXBlLCBTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249OS45OC43Ljk4NywgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05ZDc3Y2M3YWQzOWI2OGViXV0BAAAABFRlc3QENlN5c3RlbS5SdW50aW1lLlNlcmlhbGl6YXRpb24uRm9ybWF0dGVycy5UZXN0cy5Tb21lVHlwZQIAAAACAAAACQMAAAAFAwAAADZTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMuU29tZVR5cGUBAAAACVNvbWVGaWVsZAAIAgAAAAkAAAAL";
+            string serializedObj = @"AAEAAAD/////AQAAAAAAAAAMAgAAAHBTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249NS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1jYzdiMTNmZmNkMmRkZDUxBQEAAADuAVN5c3RlbS5SdW50aW1lLlNlcmlhbGl6YXRpb24uRm9ybWF0dGVycy5UZXN0cy5HZW5lcmljVHlwZVdpdGhBcmdgMVtbU3lzdGVtLlJ1bnRpbWUuU2VyaWFsaXphdGlvbi5Gb3JtYXR0ZXJzLlRlc3RzLlNvbWVUeXBlLCBTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249NS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1jYzdiMTNmZmNkMmRkZDUxXV0BAAAABFRlc3QENlN5c3RlbS5SdW50aW1lLlNlcmlhbGl6YXRpb24uRm9ybWF0dGVycy5UZXN0cy5Tb21lVHlwZQIAAAACAAAACQMAAAAFAwAAADZTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMuU29tZVR5cGUBAAAACVNvbWVGaWVsZAAIAgAAAAkAAAAL";
 
             var deserialized = (GenericTypeWithArg<SomeType>)BinaryFormatterHelpers.FromBase64String(serializedObj, FormatterAssemblyStyle.Simple);
             Assert.Equal(obj, deserialized);
index fd928d2..6db7b5f 100644 (file)
 
   <Target Name="ArchiveHelixItems"
           DependsOnTargets="GenerateProjects;CopyProducedPackages"
-          Condition="'$(ArchiveTests.ToLower())' == 'packages' or '$(ArchiveTests.ToLower())' == 'all'">
+          Condition="'$(ArchiveTests)' == 'true'">
 
     <MakeDir Directories="$(TestArchiveTestsRoot)" />
     <ZipDirectory
 
   <Target Name="RestoreProjects"
           DependsOnTargets="GenerateProjects"
-          Condition="'$(ArchiveTests.ToLower())' != 'packages' and '$(ArchiveTests.ToLower())' != 'all'">
+          Condition="'$(ArchiveTests)' != 'true'">
 
     <PropertyGroup>
       <TestRestoreCommand>$(TestDotNetPath)</TestRestoreCommand>
 
   <Target Name="BuildProjects"
           DependsOnTargets="RestoreProjects"
-          Condition="'$(ArchiveTests.ToLower())' != 'packages' and '$(ArchiveTests.ToLower())' != 'all'">
+          Condition="'$(ArchiveTests)' != 'true'">
 
     <PropertyGroup>
       <TestBuildCommand>$(TestDotNetPath)</TestBuildCommand>
index 25ed7f6..a86dfd0 100644 (file)
     </ItemGroup>
   </Target>
 
+  <UsingTask TaskName="GenerateFileVersionProps" AssemblyFile="$(InstallerTasksAssemblyPath)"/>
+  <Target Name="GenerateFileVersionProps">
+    <GenerateFileVersionProps Files="@(SharedFrameworkRuntimeFiles)"
+                              PackageId="Microsoft.NETCore.App"
+                              PackageVersion="$(ProductVersion)"
+                              PlatformManifestFile="$(PlatformManifestFile)"
+                              PreferredPackages="Microsoft.NetCore.App"
+                              PermitDllAndExeFilesLackingFileVersion="true" />
+  </Target>
+
+  <!--
+    Shared framework deps file generation.
+    Produces a test shared-framework deps file.
+    To use invoke target directly specifying NETCoreAppTestSharedFrameworkPath property.
+  -->
+  <UsingTask TaskName="GenerateTestSharedFrameworkDepsFile" AssemblyFile="$(InstallerTasksAssemblyPath)"/>
+  <Target Name="GenerateTestSharedFrameworkDepsFile">
+    <GenerateTestSharedFrameworkDepsFile SharedFrameworkDirectory="$(NETCoreAppTestSharedFrameworkPath)"
+                                         RuntimeGraphFiles="$(RuntimeIdGraphDefinitionFile)"
+                                         TargetRuntimeIdentifier="$(PackageRID)" />
+  </Target>
+
   <!-- Generate launch settings support files to enable VS debugging. -->
   <Target Name="GenerateLaunchSettingsFiles" Condition="'$(EnableLaunchSettings)' == 'true'">
     <PropertyGroup>
index 397f7de..bfb02bc 100644 (file)
@@ -21,7 +21,6 @@
     <PackageReference Include="Microsoft.NETCore.Platforms" Version="$(MicrosoftNETCorePlatformsVersion)" />
     <PackageReference Include="transport.Microsoft.NETCore.Runtime.CoreCLR" Version="$(MicrosoftNETCoreRuntimeCoreCLRVersion)" />
     <PackageReference Include="Microsoft.NETCore.TestHost" Version="$(MicrosoftNETCoreRuntimeCoreCLRVersion)" />
-    <PackageReference Include="runtime.native.System.Data.SqlClient.sni" Version="$(RuntimeNativeSystemDataSqlClientSniVersion)" />
     <PackageReference Include="Microsoft.NETCore.DotNetHost" Version="$(MicrosoftNETCoreDotNetHostVersion)" />
     <PackageReference Include="Microsoft.NETCore.DotNetHostPolicy" Version="$(MicrosoftNETCoreDotNetHostPolicyVersion)" />
     <!-- We do not need apphost.exe and the 3.0 SDK will actually remove it.
index 67e553b..c65ae3d 100644 (file)
@@ -1,12 +1,11 @@
 <Project InitialTargets="IncludeProjectReferences">
   <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" />
+  <Import Project="$(RepositoryEngineeringDir)coverage.props" Condition="'$(EnableCoverageSupport)' == 'true'" />
 
-  <PropertyGroup>
-    <EnableFullCoverageReportTarget>true</EnableFullCoverageReportTarget>
-    <GenerateFullCoverageReport>true</GenerateFullCoverageReport>
+  <PropertyGroup Condition="'$(EnableCoverageSupport)' == 'true'">
     <CoverageReportInputPath>$(ArtifactsBinDir)*.Tests/**/coverage.xml</CoverageReportInputPath>
     <CoverageReportDir>$(ArtifactsDir)coverage</CoverageReportDir>
-    <SerializeProjects Condition="'$(Coverage)' == 'true'">true</SerializeProjects>
+    <SerializeProjects>true</SerializeProjects>
   </PropertyGroup>
 
   <ItemGroup Condition="'$(BuildAllConfigurations)' != 'true'">
     </ItemGroup>
   </Target>
 
+  <Target Name="GenerateCoverageReport"
+          Condition="'$(EnableCoverageSupport)' == 'true' and '$(SkipCoverageReport)' != 'true'"
+          AfterTargets="TestAllProjects"
+          Inputs="$(CoverageReportInputPath)"
+          Outputs="$(CoverageReportResultsPath)">
+    <Exec Command="$(CoverageReportCommandLine)" />
+  </Target>
+
+  <Import Project="$(RepositoryEngineeringDir)coverage.targets" Condition="'$(EnableCoverageSupport)' == 'true'" />
   <Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" />
   
   <Target Name="Build" DependsOnTargets="BuildAllProjects" />
-  <Target Name="BuildAllProjects" DependsOnTargets="$(TraversalBuildDependsOn);FilterProjects">
+  <Target Name="BuildAllProjects" DependsOnTargets="FilterProjects">
     <MSBuild Targets="Build"
              Projects="@(Project)"
              Properties="BuildAllProjects=true;BuildConfiguration=$(BuildConfiguration);%(Project.AdditionalProperties)"
@@ -36,7 +44,7 @@
              ContinueOnError="ErrorAndStop" />
 
     <!-- Given we ErrorAndContinue we need to propagate the error if the overall task failed -->
-    <Error Condition="'$(MSBuildLastTaskResult)'=='false'" />
+    <Error Condition="'$(MSBuildLastTaskResult)' == 'false'" />
   </Target>
 
   <Target Name="Test" DependsOnTargets="TestAllProjects" />
@@ -57,7 +65,7 @@
              ContinueOnError="ErrorAndContinue" />
 
     <!-- Given we ErrorAndContinue we need to propagate the error if the overall task failed -->
-    <Error Condition="'$(MSBuildLastTaskResult)'=='false'" />
+    <Error Condition="'$(MSBuildLastTaskResult)' == 'false'" />
   </Target>
 
 </Project>
index a8cfdb8..9ded179 100644 (file)
@@ -17,7 +17,6 @@ namespace Microsoft.DotNet.Build.Tasks
         private const string PreferredPackagesProperty = "PackageConflictPreferredPackages";
         private static readonly Version ZeroVersion = new Version(0, 0, 0, 0);
 
-
         [Required]
         public ITaskItem[] Files { get; set; }
 
@@ -30,7 +29,6 @@ namespace Microsoft.DotNet.Build.Tasks
         [Required]
         public string PlatformManifestFile { get; set; }
 
-        [Required]
         public string PropsFile { get; set; }
 
         [Required]
@@ -121,13 +119,19 @@ namespace Microsoft.DotNet.Build.Tasks
                 }
             }
 
-            var props = ProjectRootElement.Create();
-            var itemGroup = props.AddItemGroup();
-            // set the platform manifest when the platform is not being published as part of the app
-            itemGroup.Condition = "'$(RuntimeIdentifier)' == '' or '$(SelfContained)' != 'true'";
+            bool generatePropsFile = !string.IsNullOrWhiteSpace(PropsFile);
+            ProjectRootElement props = null;
 
-            var manifestFileName = Path.GetFileName(PlatformManifestFile);
-            itemGroup.AddItem(PlatformManifestsItem, $"$(MSBuildThisFileDirectory){manifestFileName}");
+            if (generatePropsFile)
+            {
+                props = ProjectRootElement.Create();
+                var itemGroup = props.AddItemGroup();
+                // set the platform manifest when the platform is not being published as part of the app
+                itemGroup.Condition = "'$(RuntimeIdentifier)' == '' or '$(SelfContained)' != 'true'";
+
+                var manifestFileName = Path.GetFileName(PlatformManifestFile);
+                itemGroup.AddItem(PlatformManifestsItem, $"$(MSBuildThisFileDirectory){manifestFileName}");
+            }
 
             Directory.CreateDirectory(Path.GetDirectoryName(PlatformManifestFile));
             using (var manifestWriter = File.CreateText(PlatformManifestFile))
@@ -143,13 +147,16 @@ namespace Microsoft.DotNet.Build.Tasks
                 }
             }
 
-            var propertyGroup = props.AddPropertyGroup();
-            propertyGroup.AddProperty(PreferredPackagesProperty, PreferredPackages);
+            if (!string.IsNullOrWhiteSpace(PropsFile))
+            {
+                var propertyGroup = props.AddPropertyGroup();
+                propertyGroup.AddProperty(PreferredPackagesProperty, PreferredPackages);
 
-            var versionPropertyName = $"_{PackageId.Replace(".", "_")}_Version";
-            propertyGroup.AddProperty(versionPropertyName, PackageVersion);
+                var versionPropertyName = $"_{PackageId.Replace(".", "_")}_Version";
+                propertyGroup.AddProperty(versionPropertyName, PackageVersion);
 
-            props.Save(PropsFile);
+                props.Save(PropsFile);
+            }
 
             return !Log.HasLoggedErrors;
         }
@@ -198,4 +205,4 @@ namespace Microsoft.DotNet.Build.Tasks
             public ITaskItem File { get; set; }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tools-local/tasks/installer.tasks/GenerateRunScript.cs b/tools-local/tasks/installer.tasks/GenerateRunScript.cs
new file mode 100644 (file)
index 0000000..0dd2408
--- /dev/null
@@ -0,0 +1,114 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using System;
+using System.IO;
+using System.Text;
+
+namespace Microsoft.DotNet.Build.Tasks
+{
+    public class GenerateRunScript : Task
+    {
+        [Required]
+        public string[] RunCommands { get; set; }
+
+        [Required]
+        public string TemplatePath { get; set; }
+
+        [Required]
+        public string OutputPath { get; set; }
+
+        public override bool Execute()
+        {
+            if (RunCommands.Length == 0)
+            {
+                Log.LogError("Please provide at least one test command to execute via the RunCommands property.");
+                return false;
+            }
+
+            if (!File.Exists(TemplatePath))
+            {
+                Log.LogError($"Runner script template {TemplatePath} was not found.");
+                return false;
+            }
+
+            string templateContent = File.ReadAllText(TemplatePath);
+            Directory.CreateDirectory(Path.GetDirectoryName(OutputPath));
+
+            Log.LogMessage($"Run commands = {string.Join(Environment.NewLine, RunCommands)}");
+
+            string extension = Path.GetExtension(Path.GetFileName(OutputPath)).ToLowerInvariant();
+            switch (extension)
+            {
+                case ".sh":
+                case ".cmd":
+                case ".bat":
+                    WriteRunScript(templateContent, extension);
+                    break;
+                default:
+                    Log.LogError($"Generating runner scripts with extension '{extension}' is not supported.");
+                    return false;
+            }
+
+            return true;
+        }
+
+        private void WriteRunScript(string templateContent, string extension)
+        {
+            bool isUnix = extension == ".sh";
+            string lineFeed = isUnix ? "\n" : "\r\n";
+
+            var runCommandsBuilder = new StringBuilder();
+            for (int i = 0; i < RunCommands.Length; i++)
+            {
+                runCommandsBuilder.Append(RunCommands[i]);
+                if (i < RunCommands.Length - 1)
+                {
+                    runCommandsBuilder.Append(lineFeed);
+                }
+            }
+            templateContent = templateContent.Replace("[[RunCommands]]", runCommandsBuilder.ToString());
+
+            var runCommandEchoesBuilder = new StringBuilder();
+            foreach (string runCommand in RunCommands)
+            {
+                // Escape backtick and question mark characters to avoid running commands instead of echo'ing them.
+                string sanitizedRunCommand = runCommand.Replace("`", "\\`")
+                                                        .Replace("?", "\\")
+                                                        .Replace("\r","")
+                                                        .Replace("\n"," ")
+                                                        .Replace("&", "^&")
+                                                        .Replace(">", "^>");
+
+                if (isUnix)
+                {
+                    // Remove parentheses and quotes from echo command before wrapping it in quotes to avoid errors on Linux.
+                    sanitizedRunCommand = "\"" + sanitizedRunCommand.Replace("\"", "")
+                                        .Replace("(", "")
+                                        .Replace(")", "") + "\"";
+                }
+
+                runCommandEchoesBuilder.Append($"echo {sanitizedRunCommand}{lineFeed}");
+            }
+            templateContent = templateContent.Replace("[[RunCommandsEcho]]", runCommandEchoesBuilder.ToString());
+
+            if (isUnix)
+            {
+                // Just in case any Windows EOLs have made it in by here, clean any up.
+                templateContent = templateContent.Replace("\r\n", "\n");
+            }
+
+            using (StreamWriter sw = new StreamWriter(new FileStream(OutputPath, FileMode.Create)))
+            {
+                sw.NewLine = lineFeed;
+                sw.Write(templateContent);
+                sw.WriteLine();
+            }
+
+            Log.LogMessage($"Wrote {extension} run script to {OutputPath}");
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools-local/tasks/installer.tasks/GenerateTestSharedFrameworkDepsFile.cs b/tools-local/tasks/installer.tasks/GenerateTestSharedFrameworkDepsFile.cs
new file mode 100644 (file)
index 0000000..0048a46
--- /dev/null
@@ -0,0 +1,132 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Build.Framework;
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using Microsoft.Extensions.DependencyModel;
+using System.Collections.Generic;
+using NuGet.RuntimeModel;
+
+namespace Microsoft.DotNet.Build.Tasks
+{
+    public partial class GenerateTestSharedFrameworkDepsFile : BuildTask
+    {
+        // we don't care about these values in the deps file
+        const string rid = "rid";
+        const string tfm = "netcoreapp0.0";
+        const string fullTfm = ".NETCoreApp,Version=v0.0";
+
+        [Required]
+        public string SharedFrameworkDirectory { get; set; }
+
+        [Required]
+        public string[] RuntimeGraphFiles { get; set; }
+
+        [Required]
+        public string TargetRuntimeIdentifier { get; set; }
+
+        public override bool Execute()
+        {
+            var sharedFxDir = new DirectoryInfo(SharedFrameworkDirectory);
+            if (!sharedFxDir.Exists)
+            {
+                Log.LogError($"{nameof(SharedFrameworkDirectory)} '{SharedFrameworkDirectory}' does not exist.");
+                return false;
+            }
+
+            // directory is the version folder, parent is the shared framework name.
+            string sharedFxVersion = sharedFxDir.Name;
+            string sharedFxName = sharedFxDir.Parent.Name;
+
+            var ignoredExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
+            {
+                ".pdb",
+                ".json",
+                ".config",
+                ".xml"
+            };
+
+            var isAssemblyTofileNames = Directory.EnumerateFiles(SharedFrameworkDirectory)
+                .Where(file => !ignoredExtensions.Contains(Path.GetExtension(file)))
+                .ToLookup(file => IsManagedAssembly(file), file => Path.GetFileName(file));
+
+            var managedFileNames = isAssemblyTofileNames[true];
+            var nativeFileNames = isAssemblyTofileNames[false];
+
+            var runtimeLibraries = new[]
+            {
+                new RuntimeLibrary(
+                    type:"package",
+                    name: sharedFxName,
+                    version: sharedFxVersion,
+                    hash: "hash",
+                    runtimeAssemblyGroups: new[] { new RuntimeAssetGroup(string.Empty, managedFileNames.Select(f => $"runtimes/{rid}/lib/{tfm}/{f}")) },
+                    nativeLibraryGroups: new[] { new RuntimeAssetGroup(string.Empty, nativeFileNames.Select(f => $"runtimes/{rid}/native/{f}")) },
+                    resourceAssemblies: Enumerable.Empty<ResourceAssembly>(),
+                    dependencies: Enumerable.Empty<Dependency>(),
+                    serviceable: true)
+            };
+
+            var targetInfo = new TargetInfo(fullTfm, rid, "runtimeSignature", isPortable: false);
+
+            var runtimeFallbacks = GetRuntimeFallbacks(RuntimeGraphFiles, TargetRuntimeIdentifier);
+
+            var dependencyContext = new DependencyContext(
+                targetInfo,
+                CompilationOptions.Default,
+                Enumerable.Empty<CompilationLibrary>(),
+                runtimeLibraries,
+                runtimeFallbacks);
+
+            using (var depsFileStream = File.Create(Path.Combine(SharedFrameworkDirectory, $"{sharedFxName}.deps.json")))
+            {
+                new DependencyContextWriter().Write(dependencyContext, depsFileStream);
+            }
+
+            return !Log.HasLoggedErrors;
+        }
+
+        private static bool IsManagedAssembly(string file)
+        {
+            bool result = false;
+            try
+            {
+                using (var peReader = new PEReader(File.OpenRead(file)))
+                {
+                    result = peReader.HasMetadata && peReader.GetMetadataReader().IsAssembly;
+                }
+            }
+            catch (BadImageFormatException)
+            { }
+
+            return result;
+        }
+
+        private static IEnumerable<RuntimeFallbacks> GetRuntimeFallbacks(string[] runtimeGraphFiles, string runtime)
+        {
+            RuntimeGraph runtimeGraph = RuntimeGraph.Empty;
+
+            foreach (string runtimeGraphFile in runtimeGraphFiles)
+            {
+                runtimeGraph = RuntimeGraph.Merge(runtimeGraph, JsonRuntimeFormat.ReadRuntimeGraph(runtimeGraphFile));
+            }
+
+            foreach (string rid in runtimeGraph.Runtimes.Select(p => p.Key))
+            {
+                IEnumerable<string> ridFallback = runtimeGraph.ExpandRuntime(rid);
+
+                if (ridFallback.Contains(runtime))
+                {
+                    // ExpandRuntime return runtime itself as first item so we are skiping it
+                    yield return new RuntimeFallbacks(rid, ridFallback.Skip(1));
+                }
+            }
+        }
+
+    }
+}
index a955c7d..0c73fdc 100644 (file)
@@ -1,14 +1,12 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk" TreatAsLocalProperty="Configuration;Debug">
 
   <PropertyGroup>
     <TargetFrameworks>netstandard2.0</TargetFrameworks>
     <TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net46</TargetFrameworks>
     <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
-    <!-- Duplicating the assembly path here as CoreClr current overrides the Configuration property. -->
-    <InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$(IntermediateOutputPath)netstandard2.0\installer.tasks.dll</InstallerTasksAssemblyPath>
-    <InstallerTasksAssemblyPath Condition="'$(MSBuildRuntimeType)' != 'Core'">$(IntermediateOutputPath)net46\installer.tasks.dll</InstallerTasksAssemblyPath>
     <!-- Set platform to AnyCPU (not TargetArchitecture) to avoid arch-specific output path. -->
     <Platform>AnyCPU</Platform>
+    <Configuration>Debug</Configuration>
   </PropertyGroup>
 
   <ItemGroup>
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="NuGet.ProjectModel" Version="$(RefOnlyNugetProjectModelVersion)" />
     <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelVersion)" />
     <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="2.1.0" />
+    <PackageReference Include="NuGet.ProjectModel" Version="$(RefOnlyNugetProjectModelVersion)" />
+    <PackageReference Include="NuGet.Packaging" Version="$(RefOnlyNugetPackagingVersion)" />
+    <PackageReference Include="System.Reflection.Metadata" Version="1.7.0" />
   </ItemGroup>
 
-  <ItemGroup Condition="'$(TargetFramework)' != 'net46'">
-    <PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" />
-    <PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" />
-    <PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(RefOnlyMicrosoftBuildTasksCoreVersion)" />
-    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(RefOnlyMicrosoftBuildUtilitiesCoreVersion)" />
-
-    <PackageReference Include="System.Diagnostics.FileVersionInfo" Version="4.0.0" />
-  </ItemGroup>
-
-  <ItemGroup Condition="'$(TargetFramework)' == 'net46'">
-    <Compile Include="net46/ProcessSharedFrameworkDeps.net46.cs" />
-
-    <Reference Include="Microsoft.Build.Framework" />
-    <Reference Include="Microsoft.Build.Tasks.v4.0" />
-    <Reference Include="Microsoft.Build.Utilities.v4.0" />
-    <Reference Include="Microsoft.Build" />
-    <Reference Include="System.IO.Compression.FileSystem" />
-    <Reference Include="System.IO.Compression" />
-  </ItemGroup>
+  <Choose>
+    <When Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
+      <ItemGroup>
+        <Compile Include="net46/ProcessSharedFrameworkDeps.net46.cs" />
+      </ItemGroup>
+      <ItemGroup>
+        <Reference Include="Microsoft.Build.Framework" />
+        <Reference Include="Microsoft.Build.Tasks.v4.0" />
+        <Reference Include="Microsoft.Build.Utilities.v4.0" />
+        <Reference Include="Microsoft.Build" />
+        <Reference Include="System.IO.Compression.FileSystem" />
+        <Reference Include="System.IO.Compression" />
+      </ItemGroup>
+    </When>
+    <Otherwise>
+      <ItemGroup>
+        <PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" />
+        <PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" />
+        <PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(RefOnlyMicrosoftBuildTasksCoreVersion)" />
+        <PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(RefOnlyMicrosoftBuildUtilitiesCoreVersion)" />
+        <PackageReference Include="System.Diagnostics.FileVersionInfo" Version="4.0.0" />
+      </ItemGroup>
+    </Otherwise>
+  </Choose>
 
   <UsingTask TaskName="GetTargetMachineInfo"
              AssemblyFile="$(InstallerTasksAssemblyPath)" />