diff --git a/global.json b/global.json
index f874223fd2..a71a01938e 100644
--- a/global.json
+++ b/global.json
@@ -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"}}
diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/FrameworkConditionAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/FrameworkConditionAttribute.cs
new file mode 100644
index 0000000000..c1c102f893
--- /dev/null
+++ b/src/TestFramework/TestFramework/Attributes/TestMethod/FrameworkConditionAttribute.cs
@@ -0,0 +1,119 @@
+// 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;
+
+///
+/// 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.
+///
+///
+/// This attribute isn't inherited. Applying it to a base class will not affect derived classes.
+///
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+public sealed class FrameworkConditionAttribute : ConditionBaseAttribute
+{
+ private readonly Frameworks _frameworks;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Decides whether the frameworks will be included or excluded.
+ /// The .NET frameworks that this test includes/excludes.
+ 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}";
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The .NET frameworks that this test supports.
+ public FrameworkConditionAttribute(Frameworks frameworks)
+ : this(ConditionMode.Include, frameworks)
+ {
+ }
+
+ ///
+ /// Gets a value indicating whether the test method or test class should run.
+ ///
+ public override bool ShouldRun => IsCurrentFrameworkSupported();
+
+ ///
+ /// Gets the ignore message (in case returns ).
+ ///
+ public override string? IgnoreMessage { get; }
+
+ ///
+ /// Gets the group name for this attribute.
+ ///
+ public override string GroupName => nameof(FrameworkConditionAttribute);
+
+ private bool IsCurrentFrameworkSupported()
+ {
+ Frameworks currentFramework = GetCurrentFramework();
+ return (_frameworks & currentFramework) != 0;
+ }
+
+ private static Frameworks GetCurrentFramework()
+ {
+ string frameworkDescription = RuntimeInformation.FrameworkDescription;
+
+ // Check for UWP first as it may also have .NET Core or .NET in its description
+ if (IsRunningOnUwp())
+ {
+ return Frameworks.Uwp;
+ }
+
+ // 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+ (includes .NET 5, 6, 7, 8, 9, etc.)
+ if (frameworkDescription.StartsWith(".NET ", StringComparison.OrdinalIgnoreCase))
+ {
+ return Frameworks.Net;
+ }
+
+ // Default to .NET for unknown cases
+ return Frameworks.Net;
+ }
+
+ private static bool IsRunningOnUwp()
+ {
+ try
+ {
+ // Try to access Windows.ApplicationModel.Package.Current
+ // This is only available in UWP applications
+ var packageType = Type.GetType("Windows.ApplicationModel.Package, Windows.Runtime");
+ if (packageType is not null)
+ {
+ var currentProperty = packageType.GetProperty("Current");
+ if (currentProperty is not null)
+ {
+ var current = currentProperty.GetValue(null);
+ return current is not null;
+ }
+ }
+ }
+ catch
+ {
+ // If we can't access the UWP APIs, we're not running on UWP
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/Frameworks.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/Frameworks.cs
new file mode 100644
index 0000000000..f08c2ac8e4
--- /dev/null
+++ b/src/TestFramework/TestFramework/Attributes/TestMethod/Frameworks.cs
@@ -0,0 +1,31 @@
+// 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;
+
+///
+/// An enum that is used with to control which .NET frameworks a test method or test class supports or doesn't support.
+///
+[Flags]
+public enum Frameworks
+{
+ ///
+ /// Represents .NET Framework (the full framework on Windows).
+ ///
+ NetFramework = 1 << 0,
+
+ ///
+ /// Represents .NET Core 1.x, 2.x, and 3.x.
+ ///
+ NetCore = 1 << 1,
+
+ ///
+ /// Represents .NET 5 and later versions (unified platform).
+ ///
+ Net = 1 << 2,
+
+ ///
+ /// Represents Universal Windows Platform (UWP).
+ ///
+ Uwp = 1 << 3,
+}
\ No newline at end of file
diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt
index 766c80085b..e03c17c73b 100644
--- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt
+++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt
@@ -41,3 +41,14 @@ 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.NetCore = 2 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
+Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.NetFramework = 1 -> Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks
+Microsoft.VisualStudio.TestTools.UnitTesting.Frameworks.Uwp = 8 -> 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
diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/FrameworkConditionAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/FrameworkConditionAttributeTests.cs
new file mode 100644
index 0000000000..ec9e6e3d52
--- /dev/null
+++ b/test/UnitTests/TestFramework.UnitTests/Attributes/FrameworkConditionAttributeTests.cs
@@ -0,0 +1,277 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using FluentAssertions;
+
+using TestFramework.ForTestingMSTest;
+
+namespace UnitTestFramework.Tests;
+
+///
+/// Tests for class FrameworkConditionAttribute.
+///
+public class FrameworkConditionAttributeTests : TestContainer
+{
+ public void Constructor_SetsCorrectMode()
+ {
+ // Arrange & Act
+ var includeAttribute = new FrameworkConditionAttribute(ConditionMode.Include, Frameworks.NetFramework);
+ var excludeAttribute = new FrameworkConditionAttribute(ConditionMode.Exclude, Frameworks.NetCore);
+
+ // Assert
+ includeAttribute.Mode.Should().Be(ConditionMode.Include);
+ excludeAttribute.Mode.Should().Be(ConditionMode.Exclude);
+ }
+
+ public void Constructor_WithFrameworkOnly_DefaultsToIncludeMode()
+ {
+ // Arrange & Act
+ var attribute = new FrameworkConditionAttribute(Frameworks.Net);
+
+ // Assert
+ attribute.Mode.Should().Be(ConditionMode.Include);
+ }
+
+ public void GroupName_ReturnsCorrectValue()
+ {
+ // Arrange
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, Frameworks.Net);
+
+ // Act & Assert
+ attribute.GroupName.Should().Be(nameof(FrameworkConditionAttribute));
+ }
+
+ public void IgnoreMessage_IncludeMode_ReturnsCorrectMessage()
+ {
+ // Arrange
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, Frameworks.NetFramework);
+
+ // Act & Assert
+ attribute.IgnoreMessage.Should().Be("Test is only supported on NetFramework");
+ }
+
+ public void IgnoreMessage_ExcludeMode_ReturnsCorrectMessage()
+ {
+ // Arrange
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Exclude, Frameworks.NetCore);
+
+ // Act & Assert
+ attribute.IgnoreMessage.Should().Be("Test is not supported on NetCore");
+ }
+
+ public void IgnoreMessage_MultipleFrameworks_ReturnsCorrectMessage()
+ {
+ // Arrange
+ var frameworks = Frameworks.NetFramework | Frameworks.NetCore;
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, frameworks);
+
+ // Act & Assert
+ attribute.IgnoreMessage.Should().Be("Test is only supported on NetFramework, NetCore");
+ }
+
+ public void IgnoreMessage_UwpFramework_ReturnsCorrectMessage()
+ {
+ // Arrange
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, Frameworks.Uwp);
+
+ // Act & Assert
+ attribute.IgnoreMessage.Should().Be("Test is only supported on Uwp");
+ }
+
+ public void IgnoreMessage_ExcludeUwpFramework_ReturnsCorrectMessage()
+ {
+ // Arrange
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Exclude, Frameworks.Uwp);
+
+ // Act & Assert
+ attribute.IgnoreMessage.Should().Be("Test is not supported on Uwp");
+ }
+
+ public void ShouldRun_IncludeMode_CurrentFrameworkMatches_ReturnsTrue()
+ {
+ // Arrange
+ var currentFramework = GetCurrentFrameworkEnum();
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, currentFramework);
+
+ // Act & Assert
+ attribute.ShouldRun.Should().BeTrue();
+ }
+
+ public void ShouldRun_ExcludeMode_CurrentFrameworkMatches_ReturnsTrue()
+ {
+ // Arrange
+ var currentFramework = GetCurrentFrameworkEnum();
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Exclude, currentFramework);
+
+ // Act & Assert
+ // ShouldRun returns true when the condition is detected (current framework matches)
+ // The framework handles include/exclude logic separately
+ attribute.ShouldRun.Should().BeTrue();
+ }
+
+ public void ShouldRun_IncludeMode_CurrentFrameworkDoesNotMatch_ReturnsFalse()
+ {
+ // Arrange
+ var currentFramework = GetCurrentFrameworkEnum();
+ var differentFramework = GetDifferentFramework(currentFramework);
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, differentFramework);
+
+ // Act & Assert
+ attribute.ShouldRun.Should().BeFalse();
+ }
+
+ public void ShouldRun_ExcludeMode_CurrentFrameworkDoesNotMatch_ReturnsFalse()
+ {
+ // Arrange
+ var currentFramework = GetCurrentFrameworkEnum();
+ var differentFramework = GetDifferentFramework(currentFramework);
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Exclude, differentFramework);
+
+ // Act & Assert
+ // ShouldRun returns false when the condition is NOT detected
+ attribute.ShouldRun.Should().BeFalse();
+ }
+
+ public void ShouldRun_Net_OnCurrentDotNet_ReturnsTrue()
+ {
+ // Arrange
+ var attribute = new FrameworkConditionAttribute(Frameworks.Net);
+
+ // Act & Assert
+ // This test verifies that .NET 5+ apps match the Net flag
+ if (Environment.Version.Major >= 5 && !System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase))
+ {
+ attribute.ShouldRun.Should().BeTrue();
+ }
+ }
+
+ public void ShouldRun_MultipleFrameworks_IncludesCurrent_ReturnsTrue()
+ {
+ // Arrange
+ var currentFramework = GetCurrentFrameworkEnum();
+ var multipleFrameworks = currentFramework | Frameworks.NetFramework; // Include current plus another
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, multipleFrameworks);
+
+ // Act & Assert
+ attribute.ShouldRun.Should().BeTrue();
+ }
+
+ public void ShouldRun_MultipleFrameworks_ExcludesCurrent_ReturnsFalse()
+ {
+ // Arrange
+ var currentFramework = GetCurrentFrameworkEnum();
+ var differentFramework = GetDifferentFramework(currentFramework);
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, differentFramework);
+
+ // Act & Assert
+ attribute.ShouldRun.Should().BeFalse();
+ }
+
+ public void ShouldRun_UwpFramework_OnNonUwp_ReturnsFalse()
+ {
+ // Arrange
+ var attribute = new FrameworkConditionAttribute(Frameworks.Uwp);
+
+ // Act & Assert
+ // This test assumes we're not running on UWP in the test environment
+ if (!IsRunningOnUwp())
+ {
+ attribute.ShouldRun.Should().BeFalse();
+ }
+ }
+
+ public void ShouldRun_ExcludeUwpFramework_OnNonUwp_ReturnsTrue()
+ {
+ // Arrange
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Exclude, Frameworks.Uwp);
+
+ // Act & Assert
+ // This test assumes we're not running on UWP in the test environment
+ if (!IsRunningOnUwp())
+ {
+ attribute.ShouldRun.Should().BeFalse();
+ }
+ }
+
+ public void ShouldRun_MultipleFrameworksIncludingUwp_IncludesCurrent_ReturnsTrue()
+ {
+ // Arrange
+ var currentFramework = GetCurrentFrameworkEnum();
+ var multipleFrameworks = currentFramework | Frameworks.Uwp; // Include current plus UWP
+ var attribute = new FrameworkConditionAttribute(ConditionMode.Include, multipleFrameworks);
+
+ // Act & Assert
+ attribute.ShouldRun.Should().BeTrue();
+ }
+
+ private static Frameworks GetCurrentFrameworkEnum()
+ {
+ // Check for UWP first
+ if (IsRunningOnUwp())
+ {
+ return Frameworks.Uwp;
+ }
+
+ string frameworkDescription = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
+
+ if (frameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase))
+ {
+ return Frameworks.NetFramework;
+ }
+
+ if (frameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase))
+ {
+ return Frameworks.NetCore;
+ }
+
+ // .NET 5+
+ return Frameworks.Net;
+ }
+
+ private static bool IsRunningOnUwp()
+ {
+ try
+ {
+ // Try to access Windows.ApplicationModel.Package.Current
+ // This is only available in UWP applications
+ var packageType = Type.GetType("Windows.ApplicationModel.Package, Windows.Runtime");
+ if (packageType is not null)
+ {
+ var currentProperty = packageType.GetProperty("Current");
+ if (currentProperty is not null)
+ {
+ var current = currentProperty.GetValue(null);
+ return current is not null;
+ }
+ }
+ }
+ catch
+ {
+ // If we can't access the UWP APIs, we're not running on UWP
+ }
+
+ return false;
+ }
+
+ private static Frameworks GetDifferentFramework(Frameworks current)
+ {
+ // Return a framework that's different from the current one
+ if ((current & Frameworks.NetFramework) != 0)
+ {
+ return Frameworks.NetCore;
+ }
+ if ((current & Frameworks.NetCore) != 0)
+ {
+ return Frameworks.NetFramework;
+ }
+ if ((current & Frameworks.Net) != 0)
+ {
+ return Frameworks.NetFramework;
+ }
+ if ((current & Frameworks.Uwp) != 0)
+ {
+ return Frameworks.NetFramework;
+ }
+ return Frameworks.NetCore;
+ }
+}
\ No newline at end of file