Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 1 addition & 39 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,39 +1 @@
{
"tools": {
"dotnet": "10.0.100-preview.7.25359.101",
"runtimes": {
"dotnet": [
"3.1.32",
"6.0.36",
"7.0.20",
"8.0.14",
"9.0.3"
],
"dotnet/x86": [
"3.1.32",
"6.0.36",
"9.0.3"
],
"aspnetcore": [
"9.0.3"
]
},
"vs": {
"version": "17.8.0"
}
},
"sdk": {
"version": "10.0.100-preview.7.25359.101",
"paths": [
".dotnet",
"$host$"
],
"errorMessage": "The .NET SDK could not be found, please run ./build.cmd on Windows or ./build.sh on Linux and macOS.",
"allowPrerelease": true,
"rollForward": "latestFeature"
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25358.3",
"MSBuild.Sdk.Extras": "3.0.44"
}
}
{"sdk":{"version":"8.0.117","rollForward":"latestMinor"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Runtime.InteropServices;

namespace Microsoft.VisualStudio.TestTools.UnitTesting;

/// <summary>
/// This attribute is used to conditionally control whether a test class or a test method will run or be ignored based on the .NET framework being used.
/// </summary>
/// <remarks>
/// This attribute isn't inherited. Applying it to a base class will not affect derived classes.
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public sealed class FrameworkConditionAttribute : ConditionBaseAttribute
{
private readonly Frameworks _frameworks;

/// <summary>
/// Initializes a new instance of the <see cref="FrameworkConditionAttribute"/> class.
/// </summary>
/// <param name="mode">Decides whether the frameworks will be included or excluded.</param>
/// <param name="frameworks">The .NET frameworks that this test includes/excludes.</param>
public FrameworkConditionAttribute(ConditionMode mode, Frameworks frameworks)
: base(mode)
{
_frameworks = frameworks;
IgnoreMessage = mode == ConditionMode.Include
? $"Test is only supported on {frameworks}"
: $"Test is not supported on {frameworks}";
}

/// <summary>
/// Initializes a new instance of the <see cref="FrameworkConditionAttribute"/> class.
/// </summary>
/// <param name="frameworks">The .NET frameworks that this test supports.</param>
public FrameworkConditionAttribute(Frameworks frameworks)
: this(ConditionMode.Include, frameworks)
{
}

/// <summary>
/// Gets a value indicating whether the test method or test class should run.
/// </summary>
public override bool ShouldRun => IsCurrentFrameworkSupported();

/// <summary>
/// Gets the ignore message (in case <see cref="ShouldRun"/> returns <see langword="false"/>).
/// </summary>
public override string? IgnoreMessage { get; }

/// <summary>
/// Gets the group name for this attribute.
/// </summary>
public override string GroupName => nameof(FrameworkConditionAttribute);

private bool IsCurrentFrameworkSupported()
{
Frameworks currentFramework = GetCurrentFramework();
return (_frameworks & currentFramework) != 0;
}

private static Frameworks GetCurrentFramework()
{
string frameworkDescription = RuntimeInformation.FrameworkDescription;
Version version = Environment.Version;

// Check for .NET Framework
if (frameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase))
{
return Frameworks.NetFramework;
}

// Check for .NET Core
if (frameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase))
{
return Frameworks.NetCore;
}

// Check for .NET 5+
if (frameworkDescription.StartsWith(".NET ", StringComparison.OrdinalIgnoreCase) ||
frameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right. The check for ".NET Core" in line 82 was redundant since that case is already handled by the check on lines 75-78 which would return early. I've removed the redundant condition. Fixed in commit 4fe0d32.

{
Frameworks framework = Frameworks.Net;

// Add version-specific flags
if (version.Major >= 6)
{
framework |= Frameworks.Net6OrGreater;
}
if (version.Major >= 7)
{
framework |= Frameworks.Net7OrGreater;
}
if (version.Major >= 8)
{
framework |= Frameworks.Net8OrGreater;
}
if (version.Major >= 9)
{
framework |= Frameworks.Net9OrGreater;
}

return framework;
}

// Default to .NET for unknown cases
return Frameworks.Net;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestTools.UnitTesting;

/// <summary>
/// An enum that is used with <see cref="FrameworkConditionAttribute"/> to control which .NET frameworks a test method or test class supports or doesn't support.
/// </summary>
[Flags]
public enum Frameworks
{
/// <summary>
/// Represents .NET Framework (the full framework on Windows).
/// </summary>
NetFramework = 1 << 0,

/// <summary>
/// Represents .NET Core 1.x, 2.x, and 3.x.
/// </summary>
NetCore = 1 << 1,

/// <summary>
/// Represents .NET 5 and later versions (unified platform).
/// </summary>
Net = 1 << 2,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing. This one will be net5.0 only per the current implementation. I'm also not sure if it's good to add the versions to this enum:

  1. We will have to drop entries from the enum when we are dropping support for old target frameworks.
  2. If .NET 10 is used with an MSTest version that doesn't have an enum entry for .NET 10, what the behavior will be?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right about both concerns. I've simplified the Frameworks enum to address these issues:

  1. Removed version-specific flags: Eliminated Net6OrGreater, Net7OrGreater, Net8OrGreater, and Net9OrGreater
  2. Kept only basic framework types: NetFramework, NetCore, and Net (for .NET 5+)

This design is much cleaner and follows the same pattern as OperatingSystems enum. The Net flag now covers all .NET 5+ versions (5, 6, 7, 8, 9, etc.) without requiring new enum entries for each version. If users need version-specific logic, they can check Environment.Version in their test code.

Benefits:

  • No maintenance burden of adding/removing version flags
  • Forward compatibility (new .NET versions automatically work)
  • Simpler, less confusing API
  • Consistent with existing condition attribute patterns

Fixed in commit f3fa939.


/// <summary>
/// Represents .NET 6 and later versions.
/// </summary>
Net6OrGreater = 1 << 3,

/// <summary>
/// Represents .NET 7 and later versions.
/// </summary>
Net7OrGreater = 1 << 4,

/// <summary>
/// Represents .NET 8 and later versions.
/// </summary>
Net8OrGreater = 1 << 5,

/// <summary>
/// Represents .NET 9 and later versions.
/// </summary>
Net9OrGreater = 1 << 6,
}
14 changes: 14 additions & 0 deletions src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,17 @@ static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.Equals(obje
static Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.ReferenceEquals(object? objA, object? objB) -> bool
static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Equals(object? objA, object? objB) -> bool
static Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.ReferenceEquals(object? objA, object? objB) -> bool
Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.Net = 4 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.Net6OrGreater = 8 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.Net7OrGreater = 16 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.Net8OrGreater = 32 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.Net9OrGreater = 64 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.NetCore = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.NetFramework = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
Microsoft.VisualStudio.TestTools.UnitTesting.FrameworkConditionAttribute
Microsoft.VisualStudio.TestTools.UnitTesting.FrameworkConditionAttribute.FrameworkConditionAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.ConditionMode mode, Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks frameworks) -> void
Microsoft.VisualStudio.TestTools.UnitTesting.FrameworkConditionAttribute.FrameworkConditionAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks frameworks) -> void
override Microsoft.VisualStudio.TestTools.UnitTesting.FrameworkConditionAttribute.GroupName.get -> string!
override Microsoft.VisualStudio.TestTools.UnitTesting.FrameworkConditionAttribute.IgnoreMessage.get -> string?
override Microsoft.VisualStudio.TestTools.UnitTesting.FrameworkConditionAttribute.ShouldRun.get -> bool
Loading
Loading