Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ValueTuple for all target frameworks #4360

Merged
merged 4 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<SystemNetWebSocketsClientVersion>4.3.1</SystemNetWebSocketsClientVersion>
<SystemTextRegularExpressionsVersion>4.3.1</SystemTextRegularExpressionsVersion>
<SystemThreadingTasksExtensionsVersion>4.5.4</SystemThreadingTasksExtensionsVersion>
<SystemValueTupleVersion>4.5.0</SystemValueTupleVersion>
</PropertyGroup>
<PropertyGroup Label="Test dependencies">
<MicrosoftCodeAnalysisAnalyzerTestingVersion>1.1.3-beta1.24423.1</MicrosoftCodeAnalysisAnalyzerTestingVersion>
Expand Down Expand Up @@ -53,7 +54,7 @@
<PackageVersion Include="System.Diagnostics.TextWriterTraceListener" Version="4.3.0" />
<!-- CVE-2019-0981, CVE-2019-0980, CVE-2019-0657 -->
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
<PackageVersion Include="System.ValueTuple" Version="$(SystemValueTupleVersion)" />
</ItemGroup>
<ItemGroup Label="Test dependencies">
<PackageVersion Include="Codecov" Version="1.12.3" />
Expand Down
3 changes: 3 additions & 0 deletions samples/Playground/DebuggerUtility.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#if NETCOREAPP
#pragma warning disable CA1837 // Use 'Environment.ProcessId'
#pragma warning disable CA1416 // Validate platform compatibility

Expand Down Expand Up @@ -356,3 +357,5 @@ private static extern int NtQueryInformationProcess(
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
}

#endif
4 changes: 4 additions & 0 deletions samples/Playground/Playground.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
<NoWarn>$(NoWarn);NETSDK1023</NoWarn>
</PropertyGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
<Compile Remove="ServerMode/**/*.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="$(RepoRoot)src\Platform\Microsoft.Testing.Platform\Microsoft.Testing.Platform.csproj" />
<ProjectReference Include="$(RepoRoot)src\Adapter\MSTest.TestAdapter\MSTest.TestAdapter.csproj" />
Expand Down
11 changes: 9 additions & 2 deletions samples/Playground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
using Microsoft.Testing.Platform.Extensions.TestFramework;
using Microsoft.Testing.Platform.Extensions.TestHostControllers;
using Microsoft.Testing.Platform.Messages;
#if NETCOREAPP
using Microsoft.Testing.Platform.ServerMode.IntegrationTests.Messages.V100;
using MSTest.Acceptance.IntegrationTests.Messages.V100;
#endif
using Microsoft.Testing.Platform.TestHost;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using MSTest.Acceptance.IntegrationTests.Messages.V100;

namespace Playground;

public class Program
Expand All @@ -25,8 +26,10 @@ public static async Task<int> Main(string[] args)

if (Environment.GetEnvironmentVariable("TESTSERVERMODE") != "1")
{
#if NETCOREAPP
// To attach to the children
Microsoft.Testing.TestInfrastructure.DebuggerUtility.AttachCurrentProcessToParentVSProcess();
#endif

ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args);

Expand All @@ -49,6 +52,9 @@ public static async Task<int> Main(string[] args)
}
else
{
#if NETFRAMEWORK
throw new NotSupportedException("Server mode is not supported on .NET Framework");
#else
Environment.SetEnvironmentVariable("TESTSERVERMODE", "0");
using TestingPlatformClient client = await TestingPlatformClientFactory.StartAsServerAndConnectToTheClientAsync(Environment.ProcessPath!);

Expand All @@ -67,6 +73,7 @@ public static async Task<int> Main(string[] args)
await client.ExitAsync();

return 0;
#endif
}
}
}
Expand Down
15 changes: 9 additions & 6 deletions samples/Playground/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ public class TestClass
public TestContext TestContext { get; set; }

[TestMethod]
public void Test() => TestContext.AddResultFile(@"c:\hello2");
[DynamicData(nameof(Data))]
public void Test3(int a, int b)
=> Assert.AreNotEqual(a, b);

[TestMethod]
public void Test2() => Assert.AreEqual(1, 0, "few");

[TestMethod]
public void Test3()
public static IEnumerable<(int A, int B)> Data
{
get
{
yield return (1, 2);
yield return (3, 4);
}
}
}
71 changes: 64 additions & 7 deletions src/Adapter/MSTest.TestAdapter/DynamicDataOperations.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.VisualStudio.TestTools.UnitTesting;

#if NET471_OR_GREATER || NETCOREAPP
using System.Collections;
using System.Runtime.CompilerServices;
#endif
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
#if NET471_OR_GREATER || NETCOREAPP
using System.Runtime.CompilerServices;
#endif

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;

Expand Down Expand Up @@ -129,12 +129,12 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume
return true;
}

#if NETCOREAPP || NET471_OR_GREATER
if (dataSource is IEnumerable enumerable)
{
List<object[]> objects = new();
foreach (object? entry in enumerable)
{
#if NET471_OR_GREATER || NETCOREAPP
if (entry is not ITuple tuple
|| (objects.Count > 0 && objects[^1].Length != tuple.Length))
{
Expand All @@ -149,14 +149,71 @@ private static bool TryGetData(object dataSource, [NotNullWhen(true)] out IEnume
}

objects.Add(array);
#else
Type type = entry.GetType();
if (!IsTupleOrValueTuple(entry.GetType(), out int tupleSize)
|| (objects.Count > 0 && objects[objects.Count - 1].Length != tupleSize))
{
data = null;
return false;
}

object[] array = new object[tupleSize];
for (int i = 0; i < tupleSize; i++)
{
array[i] = type.GetField($"Item{i + 1}")?.GetValue(entry)!;
}

objects.Add(array);
#endif
}

data = objects;
return true;
}
#endif

data = null;
return false;
}

#if !NET471_OR_GREATER && !NETCOREAPP
private static bool IsTupleOrValueTuple(Type type, out int tupleSize)
{
tupleSize = 0;
if (!type.IsGenericType)
{
return false;
}

Type genericTypeDefinition = type.GetGenericTypeDefinition();

if (genericTypeDefinition == typeof(Tuple<>) ||
genericTypeDefinition == typeof(Tuple<,>) ||
genericTypeDefinition == typeof(Tuple<,,>) ||
genericTypeDefinition == typeof(Tuple<,,,>) ||
genericTypeDefinition == typeof(Tuple<,,,,>) ||
genericTypeDefinition == typeof(Tuple<,,,,,>) ||
genericTypeDefinition == typeof(Tuple<,,,,,,>) ||
genericTypeDefinition == typeof(Tuple<,,,,,,,>))
{
tupleSize = type.GetGenericArguments().Length;
return true;
}

if (genericTypeDefinition == typeof(ValueTuple<>) ||
genericTypeDefinition == typeof(ValueTuple<,>) ||
genericTypeDefinition == typeof(ValueTuple<,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,,,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,,,,,>))
{
tupleSize = type.GetGenericArguments().Length;
return true;
}

return false;
}
#endif
}
8 changes: 5 additions & 3 deletions src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.TestPlatform.AdapterUtilities" />
<PackageReference Include="System.Threading.Tasks.Extensions" Condition="'$(TargetFramework)'=='netstandard2.0' OR '$(TargetFramework)' == '$(NetFrameworkMinimum)' OR '$(TargetFramework)'=='$(UwpMinimum)'" />
<PackageReference Include="System.Threading.Tasks.Extensions" Condition=" '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == '$(NetFrameworkMinimum)' OR '$(TargetFramework)' == '$(UwpMinimum)' " />
<PackageReference Include="System.ValueTuple" Condition=" '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == '$(NetFrameworkMinimum)' OR '$(TargetFramework)' == '$(UwpMinimum)' " />
</ItemGroup>

<ItemGroup>
Expand All @@ -81,11 +82,12 @@
</ItemGroup>

<ItemGroup Label="NuGet">
<NuspecProperty Include="RepoRoot=$(RepoRoot)" />
<NuspecProperty Include="ArtifactsBinDir=$(ArtifactsBinDir)" />
<NuspecProperty Include="Configuration=$(Configuration)" />
<NuspecProperty Include="TestingPlatformVersion=$(Version.Replace('$(VersionPrefix)', '$(TestingPlatformVersionPrefix)'))" />
<NuspecProperty Include="RepoRoot=$(RepoRoot)" />
<NuspecProperty Include="SystemThreadingTasksExtensionsVersion=$(SystemThreadingTasksExtensionsVersion)" />
<NuspecProperty Include="SystemValueTupleVersion=$(SystemValueTupleVersion)" />
<NuspecProperty Include="TestingPlatformVersion=$(Version.Replace('$(VersionPrefix)', '$(TestingPlatformVersionPrefix)'))" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
<dependency id="Microsoft.Testing.Extensions.VSTestBridge" version="$TestingPlatformVersion$" />
<dependency id="Microsoft.Testing.Platform.MSBuild" version="$TestingPlatformVersion$" />
<dependency id="System.Threading.Tasks.Extensions" version="$SystemThreadingTasksExtensionsVersion$" />
<dependency id="System.ValueTuple" version="$SystemValueTupleVersion$" />
</group>
<group targetFramework="net462">
<dependency id="Microsoft.Testing.Extensions.VSTestBridge" version="$TestingPlatformVersion$" />
<dependency id="Microsoft.Testing.Platform.MSBuild" version="$TestingPlatformVersion$" />
<dependency id="System.Threading.Tasks.Extensions" version="$SystemThreadingTasksExtensionsVersion$" />
<dependency id="System.ValueTuple" version="$SystemValueTupleVersion$" />
</group>
<group targetFramework="uap10.0">
<dependency id="System.Threading.Tasks.Extensions" version="$SystemThreadingTasksExtensionsVersion$" />
<dependency id="System.ValueTuple" version="$SystemValueTupleVersion$" />
</group>
<group targetFramework="netcoreapp3.1">
<dependency id="Microsoft.Testing.Extensions.VSTestBridge" version="$TestingPlatformVersion$" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ public void GetDisplayNameForMultipleArraysOfArraysOfMultipleItems()
Verify(displayName == "TestMethod1 ([[\"a\",\"b\",\"c\"],[\"d\",\"e\",\"f\"],[\"gh\",\"ij\",\"kl\"]],['m','n','o'],[[\"1\",\"2\",\"3\"],[\"4\",\"5\",\"6\"],[\"7\",\"8\",\"9\"]])");
}

#if NETCOREAPP
public void DynamicDataSource_WithTuple_Works()
{
MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple));
Expand Down Expand Up @@ -270,38 +269,6 @@ public void DynamicDataSource_WithValueTupleWithTupleSyntax_Works()
dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Method);
dynamicDataAttribute.GetData(testMethodInfo);
}
#else
public void DynamicDataSource_WithTuple_Throws()
{
MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple));
var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Property);

VerifyThrows<ArgumentNullException>(() => dynamicDataAttribute.GetData(testMethodInfo));

dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method);
VerifyThrows<ArgumentNullException>(() => dynamicDataAttribute.GetData(testMethodInfo));
}

public void DynamicDataSource_WithValueTuple_Throws()
{
MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple));
var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Property);
VerifyThrows<ArgumentNullException>(() => dynamicDataAttribute.GetData(testMethodInfo));

dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTuple), typeof(TestClassTupleData), DynamicDataSourceType.Method);
VerifyThrows<ArgumentNullException>(() => dynamicDataAttribute.GetData(testMethodInfo));
}

public void DynamicDataSource_WithValueTupleWithTupleSyntax_Throws()
{
MethodInfo testMethodInfo = new TestClassTupleData().GetType().GetTypeInfo().GetDeclaredMethod(nameof(TestClassTupleData.DynamicDataTestWithTuple));
var dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.DataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Property);
VerifyThrows<ArgumentNullException>(() => dynamicDataAttribute.GetData(testMethodInfo));

dynamicDataAttribute = new DynamicDataAttribute(nameof(TestClassTupleData.GetDataWithValueTupleWithTupleSyntax), typeof(TestClassTupleData), DynamicDataSourceType.Method);
VerifyThrows<ArgumentNullException>(() => dynamicDataAttribute.GetData(testMethodInfo));
}
#endif
}

/// <summary>
Expand Down