|
// 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.
#nullable disable
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.Win32;
using Roslyn.Utilities;
using Xunit;
namespace Roslyn.Test.Utilities
{
/// <summary>
/// Container for common skip reasons. Secondary benefit allows us to use find all ref to
/// discover the set of tests affected by a particular scenario.
/// </summary>
public static class ConditionalSkipReason
{
public const string NoPiaNeedsDesktop = "NoPia is only supported on desktop";
public const string NetModulesNeedDesktop = "Net Modules are only supported on desktop";
public const string RestrictedTypesNeedDesktop = "Restricted types are only supported on desktop";
public const string NativePdbRequiresDesktop = "Native PDB tests can only execute on windows desktop";
public const string TestExecutionHasNewLineDependency = "Test execution depends on OS specific new lines";
/// <summary>
/// There are certain types which only appear in the desktop runtime and tests which depend on them
/// can't be run on CoreClr.
/// </summary>
public const string TestExecutionNeedsDesktopTypes = "Test execution depends on desktop types";
/// <summary>
/// There are certain types, like PermissionSet, which are only available by default in runtimes that exist
/// on Windows. These types can be added using extra assemblies but that is not done in our unit tests.
/// </summary>
public const string TestExecutionNeedsWindowsTypes = "Test execution depends on windows only types";
public const string TestExecutionHasCOMInterop = "Test execution depends on COM Interop";
public const string TestHasWindowsPaths = "Test depends on Windows style paths";
public const string TestExecutionNeedsFusion = "Test depends on desktop fusion loader API";
public const string WinRTNeedsWindowsDesktop = "WinRT is only supported on Windows desktop";
/// <summary>
/// Mono issues around Default Interface Methods
/// </summary>
public const string MonoDefaultInterfaceMethods = "Mono can't execute this default interface method test yet";
}
public class ConditionalFactAttribute : FactAttribute
{
/// <summary>
/// This property exists to prevent users of ConditionalFact from accidentally putting documentation
/// in the Skip property instead of Reason. Putting it into Skip would cause the test to be unconditionally
/// skipped vs. conditionally skipped which is the entire point of this attribute.
/// </summary>
[Obsolete("ConditionalFact should use Reason or AlwaysSkip", error: true)]
public new string Skip
{
get { return base.Skip; }
set { base.Skip = value; }
}
/// <summary>
/// Used to unconditionally Skip a test. For the rare occasion when a conditional test needs to be
/// unconditionally skipped (typically short term for a bug to be fixed).
/// </summary>
public string AlwaysSkip
{
get { return base.Skip; }
set { base.Skip = value; }
}
public string Reason { get; set; }
public ConditionalFactAttribute(params Type[] skipConditions)
{
foreach (var skipCondition in skipConditions)
{
ExecutionCondition condition = (ExecutionCondition)Activator.CreateInstance(skipCondition);
if (condition.ShouldSkip)
{
base.Skip = Reason ?? condition.SkipReason;
break;
}
}
}
}
public class ConditionalTheoryAttribute : TheoryAttribute
{
/// <summary>
/// This property exists to prevent users of ConditionalFact from accidentally putting documentation
/// in the Skip property instead of Reason. Putting it into Skip would cause the test to be unconditionally
/// skipped vs. conditionally skipped which is the entire point of this attribute.
/// </summary>
[Obsolete("ConditionalTheory should use Reason or AlwaysSkip")]
public new string Skip
{
get { return base.Skip; }
set { base.Skip = value; }
}
/// <summary>
/// Used to unconditionally Skip a test. For the rare occasion when a conditional test needs to be
/// unconditionally skipped (typically short term for a bug to be fixed).
/// </summary>
public string AlwaysSkip
{
get { return base.Skip; }
set { base.Skip = value; }
}
public string Reason { get; set; }
public ConditionalTheoryAttribute(params Type[] skipConditions)
{
foreach (var skipCondition in skipConditions)
{
ExecutionCondition condition = (ExecutionCondition)Activator.CreateInstance(skipCondition);
if (condition.ShouldSkip)
{
base.Skip = Reason ?? condition.SkipReason;
break;
}
}
}
}
public abstract class ExecutionCondition
{
public abstract bool ShouldSkip { get; }
public abstract string SkipReason { get; }
}
public static class ExecutionConditionUtil
{
public static ExecutionConfiguration Configuration =>
#if DEBUG
ExecutionConfiguration.Debug;
#elif RELEASE
ExecutionConfiguration.Release;
#else
#error Unsupported Configuration
#endif
public static bool IsWindows => Path.DirectorySeparatorChar == '\\';
public static bool IsUnix => !IsWindows;
public static bool IsMacOS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
public static bool IsDesktop => RuntimeUtilities.IsDesktopRuntime;
public static bool IsWindowsDesktop => IsWindows && IsDesktop;
// IsMonoDesktop means https://github.com/mono/mono
public static bool IsMonoDesktop => Type.GetType("Mono.Runtime") != null;
// IsMonoCore means https://github.com/dotnet/runtime/tree/main/src/mono
public static bool IsMonoCore => Type.GetType("Mono.RuntimeStructs") != null;
public static bool IsAnyMono => IsMonoCore || IsMonoDesktop;
public static bool IsCoreClr => !IsDesktop;
public static bool IsCoreClrUnix => IsCoreClr && IsUnix;
public static bool IsMonoOrCoreClr => IsMonoDesktop || IsCoreClr;
public static bool IsBitness64 => IntPtr.Size == 8;
public static bool RuntimeSupportsCovariantReturnsOfClasses => Type.GetType("System.Runtime.CompilerServices.RuntimeFeature")?.GetField("CovariantReturnsOfClasses") != null;
private static readonly Lazy<bool> s_operatingSystemRestrictsFileNames = new Lazy<bool>(() =>
{
var tempDir = Path.GetTempPath();
var path = Path.GetFullPath(Path.Combine(tempDir, "aux.txt"));
return path.StartsWith(@"\\.\", StringComparison.Ordinal);
});
/// <summary>
/// Is this a version of Windows that has ancient restrictions on file names. For example
/// prevents file names that are aux, com1, etc ...
/// </summary>
public static bool OperatingSystemRestrictsFileNames => s_operatingSystemRestrictsFileNames.Value;
}
public enum ExecutionConfiguration
{
Debug,
Release,
}
public class Bitness32 : ExecutionCondition
{
public override bool ShouldSkip => IntPtr.Size != 4;
public override string SkipReason => "Target bitness is not 32-bit";
}
public class Bitness64 : ExecutionCondition
{
public override bool ShouldSkip => IntPtr.Size != 8;
public override string SkipReason => "Target bitness is not 64-bit";
}
public class HasShiftJisDefaultEncoding : ExecutionCondition
{
public override bool ShouldSkip => Encoding.GetEncoding(0)?.CodePage != 932;
public override string SkipReason => "OS default codepage is not Shift-JIS (932).";
}
public class HasEnglishDefaultEncoding : ExecutionCondition
{
public override bool ShouldSkip
{
get
{
try
{
return Encoding.GetEncoding(0)?.CodePage != 1252;
}
catch (EntryPointNotFoundException)
{
// Mono is throwing this exception on recent runs. Need to just assume false in this case while the
// bug is tracked down.
// https://github.com/mono/mono/issues/12603
return false;
}
}
}
public override string SkipReason => "OS default codepage is not Windows-1252.";
}
public class IsEnglishLocal : ExecutionCondition
{
public static readonly IsEnglishLocal Instance = new IsEnglishLocal();
public override bool ShouldSkip
{
get
{
// WSL environments can have this value as empty string
if (string.IsNullOrEmpty(CultureInfo.CurrentCulture.Name))
{
return false;
}
return
!CultureInfo.CurrentUICulture.Name.StartsWith("en", StringComparison.OrdinalIgnoreCase) ||
!CultureInfo.CurrentCulture.Name.StartsWith("en", StringComparison.OrdinalIgnoreCase);
}
}
public override string SkipReason => "Current culture is not en";
}
public class IsRelease : ExecutionCondition
{
#if DEBUG
public override bool ShouldSkip => true;
#else
public override bool ShouldSkip => false;
#endif
public override string SkipReason => "Test not supported in DEBUG";
}
public class IsNot32BitDebug : ExecutionCondition
{
#if DEBUG
public override bool ShouldSkip => !Environment.Is64BitProcess;
#else
public override bool ShouldSkip => false;
#endif
public override string SkipReason => "Test not supported in 32bit DEBUG";
}
public class IsDebug : ExecutionCondition
{
#if DEBUG
public override bool ShouldSkip => false;
#else
public override bool ShouldSkip => true;
#endif
public override string SkipReason => "Test not supported in RELEASE";
}
public class WindowsOnly : ExecutionCondition
{
public override bool ShouldSkip => !ExecutionConditionUtil.IsWindows;
public override string SkipReason => "Test not supported on Mac and Linux";
}
public class WindowsDesktopOnly : ExecutionCondition
{
public override bool ShouldSkip => !ExecutionConditionUtil.IsWindowsDesktop;
public override string SkipReason => "Test only supported on Windows desktop";
}
public class CovariantReturnRuntimeOnly : ExecutionCondition
{
public override bool ShouldSkip => !ExecutionConditionUtil.RuntimeSupportsCovariantReturnsOfClasses;
public override string SkipReason => "Test only supported on runtimes that support covariant returns";
}
public class UnixLikeOnly : ExecutionCondition
{
public override bool ShouldSkip => !PathUtilities.IsUnixLikePlatform;
public override string SkipReason => "Test not supported on Windows";
}
public class WindowsOrMacOSOnly : ExecutionCondition
{
public override bool ShouldSkip => PathUtilities.IsUnixLikePlatform && !ExecutionConditionUtil.IsMacOS;
public override string SkipReason => "Test not supported on Linux";
}
public class WindowsOrLinuxOnly : ExecutionCondition
{
public override bool ShouldSkip => ExecutionConditionUtil.IsMacOS;
public override string SkipReason => "Test not supported on macOS";
}
public class LinuxOnly : ExecutionCondition
{
public override bool ShouldSkip => !ExecutionConditionUtil.IsLinux;
public override string SkipReason => "Test not supported on Windows or macOS";
}
public class ClrOnly : ExecutionCondition
{
public override bool ShouldSkip => MonoHelpers.IsRunningOnMono();
public override string SkipReason => "Test not supported on Mono";
}
public class NotOnMonoCore : ExecutionCondition
{
public override bool ShouldSkip => MonoHelpers.IsRunningOnMonoCore();
public override string SkipReason => "Test not supported on Mono Core";
}
public class NotOnAnyMono : ExecutionCondition
{
public override bool ShouldSkip => ExecutionConditionUtil.IsAnyMono;
public override string SkipReason => "Test not supported on Mono core or Mono Desktop";
}
public class CoreClrOnly : ExecutionCondition
{
public override bool ShouldSkip => !ExecutionConditionUtil.IsCoreClr;
public override string SkipReason => "Test only supported on CoreClr";
}
public class DesktopOnly : ExecutionCondition
{
public override bool ShouldSkip => !ExecutionConditionUtil.IsDesktop;
public override string SkipReason => "Test not supported on CoreCLR";
}
public class DesktopClrOnly : ExecutionCondition
{
public override bool ShouldSkip => MonoHelpers.IsRunningOnMono() || !ExecutionConditionUtil.IsDesktop;
public override string SkipReason => "Test not supported on Mono or CoreCLR";
}
public class MonoOrCoreClrOnly : ExecutionCondition
{
public override bool ShouldSkip => !ExecutionConditionUtil.IsMonoOrCoreClr;
public override string SkipReason => "Test only supported on Mono or CoreClr";
}
public class NoIOperationValidation : ExecutionCondition
{
public override bool ShouldSkip => CompilationExtensions.EnableVerifyIOperation;
public override string SkipReason => "Test not supported in ROSLYN_TEST_IOPERATION";
}
public class NoUsedAssembliesValidation : ExecutionCondition
{
public override bool ShouldSkip => CompilationExtensions.EnableVerifyUsedAssemblies;
public override string SkipReason => "Test not supported in ROSLYN_TEST_USEDASSEMBLIES";
}
public class OSVersionWin8 : ExecutionCondition
{
public override bool ShouldSkip => !OSVersion.IsWin8;
public override string SkipReason => "Window Version is not at least Win8 (build:9200)";
}
public class Framework35Installed : ExecutionCondition
{
public override bool ShouldSkip
{
get
{
#if NET46
try
{
const string RegistryPath = @"Software\Microsoft\NET Framework Setup\NDP\v3.5";
var key = Registry.LocalMachine.OpenSubKey(RegistryPath);
if (key == null)
{
return true;
}
var value = Convert.ToInt32(key.GetValue("Install", 0) ?? 0);
return value == 0;
}
catch
{
return true;
}
#else
return false;
#endif
}
}
public override string SkipReason => ".NET Framework 3.5 is not installed";
}
public class NotFramework45 : ExecutionCondition
{
public override bool ShouldSkip
{
get
{
// On Framework 4.5, ExtensionAttribute lives in mscorlib...
return typeof(System.Runtime.CompilerServices.ExtensionAttribute).GetTypeInfo().Assembly ==
typeof(object).GetTypeInfo().Assembly;
}
}
public override string SkipReason => "Test currently not supported on Framework 4.5";
}
}
|