[wasm] Addressing System.Runtime.Extensions failures (#38996)
authorMitchell Hwang <mitchhwang1418@gmail.com>
Mon, 13 Jul 2020 20:30:36 +0000 (16:30 -0400)
committerGitHub <noreply@github.com>
Mon, 13 Jul 2020 20:30:36 +0000 (22:30 +0200)
Co-authored-by: Mitchell Hwang <mitchell.hwang@microsoft.com>
Co-authored-by: Steve Pfister <steve.pfister@microsoft.com>
Co-authored-by: Alexander KoĢˆplinger <alex.koeplinger@outlook.com>
13 files changed:
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.Browser.cs [deleted file]
src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs
src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs [new file with mode: 0644]
src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs
src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs
src/libraries/System.Runtime.Extensions/tests/System/Environment.MachineName.cs
src/libraries/System.Runtime.Extensions/tests/System/Environment.StackTrace.cs
src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs
src/libraries/System.Runtime.Extensions/tests/System/IO/PathTests.cs
src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs
src/libraries/tests.proj

index 8989304134d4ff1c2c2d9cb9daab7d741091546a..fd27062f4c00982105c71c68eb5dbea073b80157 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Stopwatch.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\RuntimeEventSourceHelper.Unix.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.NoRegistry.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.UnixOrBrowser.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.Unix.cs" Condition="'$(IsOSXLike)' != 'true' and '$(TargetsBrowser)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.GetFolderPathCore.Unix.cs" Condition="'$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Threading\TimerQueue.Unix.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.cs" />
   </ItemGroup>
+  <ItemGroup Condition="'$(TargetsUnix)' == 'true'">
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
+  </ItemGroup>
   <ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
-    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.Browser.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Browser.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(IsOSXLike)' == 'true'">
     <Compile Include="$(CommonPath)Interop\OSX\Interop.libobjc.cs">
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs
new file mode 100644 (file)
index 0000000..9ef90da
--- /dev/null
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System
+{
+    public static partial class Environment
+    {
+        // Emscripten VFS mounts at / and is the only drive
+        public static string[] GetLogicalDrives() => new string[] { "/" };
+
+        // In the mono runtime, this maps to gethostname, which returns 'emscripten'.
+        // Returning the value here allows us to exclude more of the runtime.
+        public static string MachineName => "localhost";
+
+        // Matching what we returned for an earlier release.  There isn't an established equivalent
+        // on wasm.
+        public static long WorkingSet => 0;
+
+        public static string UserName => "Browser";
+
+        private static OperatingSystem GetOSVersion()
+        {
+            return new OperatingSystem(PlatformID.Other, new Version(1, 0, 0, 0));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.Browser.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.Browser.cs
deleted file mode 100644 (file)
index 9dd80cb..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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.
-
-namespace System
-{
-    public static partial class Environment
-    {
-        private static OperatingSystem GetOSVersion()
-        {
-            return new OperatingSystem(PlatformID.Other, new Version(1, 0, 0, 0));
-        }
-    }
-}
index 273d04bae8a1802e7be963b73a7d9778cf929736..87306e7b741afa903f53356eeaa2688d70b0a826 100644 (file)
@@ -12,44 +12,8 @@ namespace System
 {
     public static partial class Environment
     {
-        public static bool UserInteractive => true;
-
-        private static string CurrentDirectoryCore
-        {
-            get => Interop.Sys.GetCwd();
-            set => Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirectory: true);
-        }
-
-        private static string ExpandEnvironmentVariablesCore(string name)
-        {
-            var result = new ValueStringBuilder(stackalloc char[128]);
-
-            int lastPos = 0, pos;
-            while (lastPos < name.Length && (pos = name.IndexOf('%', lastPos + 1)) >= 0)
-            {
-                if (name[lastPos] == '%')
-                {
-                    string key = name.Substring(lastPos + 1, pos - lastPos - 1);
-                    string? value = GetEnvironmentVariable(key);
-                    if (value != null)
-                    {
-                        result.Append(value);
-                        lastPos = pos + 1;
-                        continue;
-                    }
-                }
-                result.Append(name.AsSpan(lastPos, pos - lastPos));
-                lastPos = pos;
-            }
-            result.Append(name.AsSpan(lastPos));
-
-            return result.ToString();
-        }
-
         public static string[] GetLogicalDrives() => Interop.Sys.GetAllMountPoints();
 
-        private static bool Is64BitOperatingSystemWhen32BitProcess => false;
-
         public static string MachineName
         {
             get
@@ -60,13 +24,24 @@ namespace System
             }
         }
 
-        private static int GetCurrentProcessId() => Interop.Sys.GetPid();
-
-        internal const string NewLineConst = "\n";
-
-        public static string SystemDirectory => GetFolderPathCore(SpecialFolder.System, SpecialFolderOption.None);
+        public static long WorkingSet
+        {
+            get
+            {
+                Type? processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
+                if (processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) is IDisposable currentProcess)
+                {
+                    using (currentProcess)
+                    {
+                        if (processType!.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null) is long result)
+                            return result;
+                    }
+                }
 
-        public static int SystemPageSize => CheckedSysConf(Interop.Sys.SysConfName._SC_PAGESIZE);
+                // Could not get the current working set.
+                return 0;
+            }
+        }
 
         public static unsafe string UserName
         {
@@ -135,40 +110,5 @@ namespace System
             // Otherwise, fail.
             throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
         }
-
-        public static string UserDomainName => MachineName;
-
-        /// <summary>Invoke <see cref="Interop.Sys.SysConf"/>, throwing if it fails.</summary>
-        private static int CheckedSysConf(Interop.Sys.SysConfName name)
-        {
-            long result = Interop.Sys.SysConf(name);
-            if (result == -1)
-            {
-                Interop.ErrorInfo errno = Interop.Sys.GetLastErrorInfo();
-                throw errno.Error == Interop.Error.EINVAL ?
-                    new ArgumentOutOfRangeException(nameof(name), name, errno.GetErrorMessage()) :
-                    Interop.GetIOException(errno);
-            }
-            return (int)result;
-        }
-
-        public static long WorkingSet
-        {
-            get
-            {
-                Type? processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
-                if (processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) is IDisposable currentProcess)
-                {
-                    using (currentProcess)
-                    {
-                        if (processType!.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null) is long result)
-                            return result;
-                    }
-                }
-
-                // Could not get the current working set.
-                return 0;
-            }
-        }
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs
new file mode 100644 (file)
index 0000000..cddf1b9
--- /dev/null
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+
+namespace System
+{
+    public static partial class Environment
+    {
+        public static bool UserInteractive => true;
+
+        private static string CurrentDirectoryCore
+        {
+            get => Interop.Sys.GetCwd();
+            set => Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirectory: true);
+        }
+
+        private static string ExpandEnvironmentVariablesCore(string name)
+        {
+            var result = new ValueStringBuilder(stackalloc char[128]);
+
+            int lastPos = 0, pos;
+            while (lastPos < name.Length && (pos = name.IndexOf('%', lastPos + 1)) >= 0)
+            {
+                if (name[lastPos] == '%')
+                {
+                    string key = name.Substring(lastPos + 1, pos - lastPos - 1);
+                    string? value = GetEnvironmentVariable(key);
+                    if (value != null)
+                    {
+                        result.Append(value);
+                        lastPos = pos + 1;
+                        continue;
+                    }
+                }
+                result.Append(name.AsSpan(lastPos, pos - lastPos));
+                lastPos = pos;
+            }
+            result.Append(name.AsSpan(lastPos));
+
+            return result.ToString();
+        }
+
+        private static bool Is64BitOperatingSystemWhen32BitProcess => false;
+
+        private static int GetCurrentProcessId() => Interop.Sys.GetPid();
+
+        internal const string NewLineConst = "\n";
+
+        public static string SystemDirectory => GetFolderPathCore(SpecialFolder.System, SpecialFolderOption.None);
+
+        public static int SystemPageSize => CheckedSysConf(Interop.Sys.SysConfName._SC_PAGESIZE);
+
+        public static string UserDomainName => MachineName;
+
+        /// <summary>Invoke <see cref="Interop.Sys.SysConf"/>, throwing if it fails.</summary>
+        private static int CheckedSysConf(Interop.Sys.SysConfName name)
+        {
+            long result = Interop.Sys.SysConf(name);
+            if (result == -1)
+            {
+                Interop.ErrorInfo errno = Interop.Sys.GetLastErrorInfo();
+                throw errno.Error == Interop.Error.EINVAL ?
+                    new ArgumentOutOfRangeException(nameof(name), name, errno.GetErrorMessage()) :
+                    Interop.GetIOException(errno);
+            }
+            return (int)result;
+        }
+    }
+}
index b3d13e3d8776a7a1494cbb16231c49e9b10c267d..3fa95271ee2c504129f634cc2e5f8e5109b7112c 100644 (file)
@@ -44,6 +44,7 @@ namespace System.Tests
         }
 
         [Fact]
+        [PlatformSpecific(~TestPlatforms.Browser)] // throws pNSE
         public void TargetFrameworkTest()
         {
             const int ExpectedExitCode = 0;
@@ -129,7 +130,7 @@ namespace System.Tests
 
             // GetEntryAssembly may be null (i.e. desktop)
             if (expected == null)
-                expected = Assembly.GetExecutingAssembly().GetName().Name;
+                expected = "DefaultDomain";
 
             Assert.Equal(expected, s);
         }
@@ -371,6 +372,7 @@ namespace System.Tests
         }
 
         [Fact]
+        [PlatformSpecific(~TestPlatforms.Browser)] // Throws PNSE
         public void MonitoringIsEnabled()
         {
             Assert.True(AppDomain.MonitoringIsEnabled);
index 34fde94009cacc3bccde0dafda5f03e98a24d3ed..0ba249373cc7d5868c626dcc187e21258d5e0e60 100644 (file)
@@ -41,6 +41,7 @@ namespace System.Tests
         [InlineData(1)] // setting ExitCode and exiting Main
         [InlineData(2)] // setting ExitCode both from Main and from an Unloading event handler.
         [InlineData(3)] // using Exit(exitCode)
+        [PlatformSpecific(~TestPlatforms.Browser)] // throws PNSE
         public static void ExitCode_VoidMainAppReturnsSetValue(int mode)
         {
             int expectedExitCode = 123;
index 1d12af5cbd53f1f971616a2c4289f56b3013f8cf..eac8bf18bb1206d93060762fa7a68ee2e968f38d 100644 (file)
@@ -20,6 +20,8 @@ namespace System.Tests
 #if !Unix
                 return Environment.GetEnvironmentVariable("COMPUTERNAME");
 #else
+                if (PlatformDetection.IsBrowser)
+                    return "localhost";
                 string temp = Interop.Sys.GetNodeName();
                 int index = temp.IndexOf('.');
                 return index < 0 ? temp : temp.Substring(0, index);
index e3fa2c36d28f7799c868a639af298df4ec87b743..d8505dd4e11c73e341ba55c0a8a25a3193a5bdc3 100644 (file)
@@ -68,6 +68,7 @@ namespace System.Tests
         }
 
         [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/38995", TestPlatforms.Browser)]
         public void StackTraceDoesNotStartWithInternalFrame()
         {
              string stackTrace = Environment.StackTrace;
index 3c7886134122ee294d685eafe57ff1c55f38ddc0..ff8eb26cd2435b2d791f1e0fddc71c800b4afb53 100644 (file)
@@ -221,7 +221,10 @@ namespace System.Tests
         [Fact]
         public void WorkingSet_Valid()
         {
-            Assert.True(Environment.WorkingSet > 0, "Expected positive WorkingSet value");
+            if (PlatformDetection.IsBrowser)
+                Assert.Equal(0, Environment.WorkingSet);
+            else
+                Assert.True(Environment.WorkingSet > 0, "Expected positive WorkingSet value");
         }
 
         [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)] // fail fast crashes the process
index 45023c0873fdea222d51e030619ad723129db89d..2ef46ada0d0b67e5b6503218b9631a929d159b38 100644 (file)
@@ -216,8 +216,6 @@ namespace System.IO.Tests
                     { currentDirectory, currentDirectory },
                     // "." => current directory
                     { ".", currentDirectory },
-                    // ".." => up a directory
-                    { "..", Path.GetDirectoryName(currentDirectory) },
                     // "dir/./././." => "dir"
                     { Path.Combine(currentDirectory, ".", ".", ".", ".", "."), currentDirectory },
                     // "dir///." => "dir"
@@ -234,6 +232,12 @@ namespace System.IO.Tests
                     { root + new string(Path.DirectorySeparatorChar, 3), root },
                 };
 
+                if (currentDirectory != Path.GetPathRoot(currentDirectory))
+                {
+                    // ".." => up a directory
+                    data.Add("..", Path.GetDirectoryName(currentDirectory));
+                }
+
                 // Path longer than MaxPath that normalizes down to less than MaxPath
                 const int Iters = 10000;
                 var longPath = new StringBuilder(currentDirectory, currentDirectory.Length + (Iters * 2));
index b60edf9c26fbede49a1aa91858a805a18b41f689..62edd56f51cb087fdcce35f596274e36cd9074a5 100644 (file)
@@ -86,8 +86,12 @@ namespace System.Tests
             //                          lower                upper          Culture
             yield return new object[] { "abcd",             "ABCD",         "en-US" };
             yield return new object[] { "latin i",          "LATIN I",      "en-US" };
-            yield return new object[] { "turky \u0131",     "TURKY I",      "tr-TR" };
-            yield return new object[] { "turky i",          "TURKY \u0130", "tr-TR" };
+
+            if (PlatformDetection.IsNotInvariantGlobalization)
+            {
+                yield return new object[] { "turky \u0131",     "TURKY I",      "tr-TR" };
+                yield return new object[] { "turky i",          "TURKY \u0130", "tr-TR" };
+            }
         }
 
         [Theory]
@@ -159,27 +163,28 @@ namespace System.Tests
             AssertExtensions.Throws<ArgumentException>("comparisonType", () => StringComparer.FromComparison(maxInvalid));
         }
 
-        public static TheoryData<string, string, string, CompareOptions, bool> CreateFromCultureAndOptionsData => new TheoryData<string, string, string, CompareOptions, bool>
-        {
-            { "abcd", "ABCD", "en-US", CompareOptions.None, false},
-            { "latin i", "LATIN I", "en-US", CompareOptions.None, false},
-            { "turky \u0131", "TURKY I", "tr-TR", CompareOptions.None, false},
-            { "turky i", "TURKY \u0130", "tr-TR", CompareOptions.None, false},
-            { "abcd", "ABCD", "en-US", CompareOptions.IgnoreCase, true},
-            { "latin i", "LATIN I", "en-US", CompareOptions.IgnoreCase, true},
-            { "turky \u0131", "TURKY I", "tr-TR", CompareOptions.IgnoreCase, true},
-            { "turky i", "TURKY \u0130", "tr-TR", CompareOptions.IgnoreCase, true},
-            { "abcd", "ab cd", "en-US", CompareOptions.IgnoreSymbols, true },
-            { "abcd", "ab+cd", "en-US", CompareOptions.IgnoreSymbols, true },
-            { "abcd", "ab%cd", "en-US", CompareOptions.IgnoreSymbols, true },
-            { "abcd", "ab&cd", "en-US", CompareOptions.IgnoreSymbols, true },
-            { "abcd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
-            { "abcd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
-            { "a-bcd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
-            { "abcd*", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
-            { "ab$dd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, false },
-            { "abcd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
-        };
+        public static TheoryData<string, string, string, CompareOptions, bool> CreateFromCultureAndOptionsData => 
+            new TheoryData<string, string, string, CompareOptions, bool>
+            {
+                { "abcd", "ABCD", "en-US", CompareOptions.None, false},
+                { "latin i", "LATIN I", "en-US", CompareOptions.None, false},
+                { "turky \u0131", "TURKY I", "tr-TR", CompareOptions.None, false},
+                { "turky i", "TURKY \u0130", "tr-TR", CompareOptions.None, false},
+                { "abcd", "ABCD", "en-US", CompareOptions.IgnoreCase, true},
+                { "latin i", "LATIN I", "en-US", CompareOptions.IgnoreCase, true},
+                { "turky \u0131", "TURKY I", "tr-TR", CompareOptions.IgnoreCase, true},
+                { "turky i", "TURKY \u0130", "tr-TR", CompareOptions.IgnoreCase, true},
+                { "abcd", "ab cd", "en-US", CompareOptions.IgnoreSymbols, true },
+                { "abcd", "ab+cd", "en-US", CompareOptions.IgnoreSymbols, true },
+                { "abcd", "ab%cd", "en-US", CompareOptions.IgnoreSymbols, true },
+                { "abcd", "ab&cd", "en-US", CompareOptions.IgnoreSymbols, true },
+                { "abcd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
+                { "abcd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
+                { "a-bcd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
+                { "abcd*", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
+                { "ab$dd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, false },
+                { "abcd", "ab$cd", "en-US", CompareOptions.IgnoreSymbols, true },
+            };
 
         public static TheoryData<string, string, string, CompareOptions, bool> CreateFromCultureAndOptionsStringSortData => new TheoryData<string, string, string, CompareOptions, bool>
         {
@@ -187,7 +192,7 @@ namespace System.Tests
             { "abcd", "ABcd", "en-US", CompareOptions.StringSort, false },
         };
 
-        [Theory]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
         [MemberData(nameof(CreateFromCultureAndOptionsData))]
         [MemberData(nameof(CreateFromCultureAndOptionsStringSortData))]
         public static void CreateFromCultureAndOptions(string actualString, string expectedString, string cultureName, CompareOptions options, bool result)
@@ -199,7 +204,7 @@ namespace System.Tests
             Assert.Equal(result, sc.Equals((object)actualString, (object)expectedString));
         }
 
-        [Theory]
+        [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
         [MemberData(nameof(CreateFromCultureAndOptionsData))]
         public static void CreateFromCultureAndOptionsStringSort(string actualString, string expectedString, string cultureName, CompareOptions options, bool result)
         {
index 29a5714bb803724f9a4a7bb2e6307c0924e06234..ec074e10721d432e8f62351cd9de8e08b1fdad7d 100644 (file)
@@ -65,7 +65,6 @@
     <ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Resources.Extensions\tests\System.Resources.Extensions.Tests.csproj" />
     <ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Resources.Reader\tests\System.Resources.Reader.Tests.csproj" />
     <ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Resources.ResourceManager\tests\System.Resources.ResourceManager.Tests.csproj" />
-    <ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Runtime.Extensions\tests\System.Runtime.Extensions.Tests.csproj" />
     <ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Runtime.InteropServices\tests\System.Runtime.InteropServices.Tests.csproj" />
     <ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Runtime.Loader\tests\DefaultContext\System.Runtime.Loader.DefaultContext.Tests.csproj" />
     <ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Runtime.Loader\tests\System.Runtime.Loader.Tests.csproj" />