From 927ced16e83c1355477ce7f0d296ad89f5afae7a Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Mon, 13 Jul 2020 16:30:36 -0400 Subject: [PATCH] [wasm] Addressing System.Runtime.Extensions failures (#38996) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mitchell Hwang Co-authored-by: Steve Pfister Co-authored-by: Alexander KoĢˆplinger --- .../System.Private.CoreLib.Shared.projitems | 7 +- .../src/System/Environment.Browser.cs | 26 +++++ .../System/Environment.OSVersion.Browser.cs | 14 --- .../src/System/Environment.Unix.cs | 94 ++++--------------- .../src/System/Environment.UnixOrBrowser.cs | 75 +++++++++++++++ .../tests/System/AppDomainTests.cs | 4 +- .../tests/System/Environment.Exit.cs | 1 + .../tests/System/Environment.MachineName.cs | 2 + .../tests/System/Environment.StackTrace.cs | 1 + .../tests/System/EnvironmentTests.cs | 5 +- .../tests/System/IO/PathTests.cs | 8 +- .../tests/System/StringComparer.cs | 55 ++++++----- src/libraries/tests.proj | 1 - 13 files changed, 170 insertions(+), 123 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.Browser.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 8989304134d4f..fd27062f4c009 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1791,7 +1791,7 @@ - + @@ -1819,8 +1819,11 @@ + + + - + 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 index 0000000000000..9ef90da4762d6 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs @@ -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 index 9dd80cbbd579e..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.OSVersion.Browser.cs +++ /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)); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs index 273d04bae8a18..87306e7b741af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -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 @@ public static string MachineName } } - 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 @@ private static unsafe bool TryGetUserNameFromPasswd(byte* buf, int bufLen, out s // Otherwise, fail. throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); } - - public static string UserDomainName => MachineName; - - /// Invoke , throwing if it fails. - 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 index 0000000000000..cddf1b929e63f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.UnixOrBrowser.cs @@ -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; + + /// Invoke , throwing if it fails. + 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; + } + } +} diff --git a/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs index b3d13e3d8776a..3fa95271ee2c5 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs @@ -44,6 +44,7 @@ public void RelativeSearchPath_Is_Null() } [Fact] + [PlatformSpecific(~TestPlatforms.Browser)] // throws pNSE public void TargetFrameworkTest() { const int ExpectedExitCode = 0; @@ -129,7 +130,7 @@ public void FriendlyName() // 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 @@ public void ReflectionOnlyGetAssemblies() } [Fact] + [PlatformSpecific(~TestPlatforms.Browser)] // Throws PNSE public void MonitoringIsEnabled() { Assert.True(AppDomain.MonitoringIsEnabled); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs index 34fde94009cac..0ba249373cc7d 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs @@ -41,6 +41,7 @@ public static void ExitCode_Roundtrips(int exitCode) [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; diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.MachineName.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.MachineName.cs index 1d12af5cbd53f..eac8bf18bb120 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.MachineName.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.MachineName.cs @@ -20,6 +20,8 @@ internal static string GetComputerName() #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); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.StackTrace.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.StackTrace.cs index e3fa2c36d28f7..d8505dd4e11c7 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.StackTrace.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.StackTrace.cs @@ -68,6 +68,7 @@ public TestClass() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/38995", TestPlatforms.Browser)] public void StackTraceDoesNotStartWithInternalFrame() { string stackTrace = Environment.StackTrace; diff --git a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs index 3c7886134122e..ff8eb26cd2435 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs @@ -221,7 +221,10 @@ public void Version_Valid() [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 diff --git a/src/libraries/System.Runtime.Extensions/tests/System/IO/PathTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/IO/PathTests.cs index 45023c0873fde..2ef46ada0d0b6 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/IO/PathTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/IO/PathTests.cs @@ -216,8 +216,6 @@ public static TheoryData GetFullPath_BasicExpansions { currentDirectory, currentDirectory }, // "." => current directory { ".", currentDirectory }, - // ".." => up a directory - { "..", Path.GetDirectoryName(currentDirectory) }, // "dir/./././." => "dir" { Path.Combine(currentDirectory, ".", ".", ".", ".", "."), currentDirectory }, // "dir///." => "dir" @@ -234,6 +232,12 @@ public static TheoryData GetFullPath_BasicExpansions { 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)); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs b/src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs index b60edf9c26fbe..62edd56f51cb0 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs @@ -86,8 +86,12 @@ public static IEnumerable UpperLowerCasing_TestData() // 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 @@ public static void FromComparisonInvalidTest() AssertExtensions.Throws("comparisonType", () => StringComparer.FromComparison(maxInvalid)); } - public static TheoryData CreateFromCultureAndOptionsData => new TheoryData - { - { "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 CreateFromCultureAndOptionsData => + new TheoryData + { + { "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 CreateFromCultureAndOptionsStringSortData => new TheoryData { @@ -187,7 +192,7 @@ public static void FromComparisonInvalidTest() { "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 @@ public static void CreateFromCultureAndOptions(string actualString, string expec 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) { diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 29a5714bb8037..ec074e10721d4 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -65,7 +65,6 @@ -