Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,37 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting.Internal;

internal static class TestDataSourceUtilities
{
private const int MaxCachedBuilderCapacity = 360;

#pragma warning disable IDE0028 // ConditionalWeakTable is not collection-expression-constructible on .NET Framework (CS9174).
private static readonly ConditionalWeakTable<MethodInfo, MethodData> MethodDataCache = new();
#pragma warning restore IDE0028

[ThreadStatic]
private static StringBuilder? s_cachedBuilder;

[ThreadStatic]
private static CultureInfo? s_cachedResourceCulture;

[ThreadStatic]
private static string? s_cachedDisplayNameFormat;

public static string? ComputeDefaultDisplayName(MethodInfo methodInfo, object?[]? data)
{
if (data is null)
{
return null;
}

ParameterInfo[] parameters = methodInfo.GetParameters();
MethodData methodData = MethodDataCache.GetValue(methodInfo, static method => new(method));
string methodDisplayName = methodInfo is ReflectionTestMethodInfo reflectionTestMethodInfo
? reflectionTestMethodInfo.DisplayName
: methodInfo.Name;
CultureInfo currentCulture = CultureInfo.CurrentCulture;
string displayNameFormat = FrameworkMessages.DataDrivenResultDisplayName;
string displayNameFormat = GetDisplayNameFormat();

var argumentsBuilder = new StringBuilder();
if (parameters.Length == 1 && parameters[0].ParameterType == typeof(object[]))
StringBuilder argumentsBuilder = AcquireBuilder();
if (methodData.HasSingleObjectArrayParameter)
{
AppendHumanizedArgument(argumentsBuilder, data);
}
Expand All @@ -29,11 +44,64 @@ internal static class TestDataSourceUtilities
AppendHumanizedArguments(argumentsBuilder, data);
}

string arguments = GetStringAndReleaseBuilder(argumentsBuilder);
return string.Format(
currentCulture,
displayNameFormat,
methodDisplayName,
argumentsBuilder.ToString());
arguments);
}

private static string GetDisplayNameFormat()
{
CultureInfo resourceCulture = FrameworkMessages.Culture ?? CultureInfo.CurrentUICulture;
if (!resourceCulture.Equals(s_cachedResourceCulture))
{
s_cachedResourceCulture = resourceCulture;
s_cachedDisplayNameFormat = FrameworkMessages.DataDrivenResultDisplayName;
Comment thread
Evangelink marked this conversation as resolved.
}

return s_cachedDisplayNameFormat!;
}

private static StringBuilder AcquireBuilder()
{
StringBuilder? builder = s_cachedBuilder;
if (builder is null)
{
return new StringBuilder();
}

s_cachedBuilder = null;
builder.Clear();
return builder;
}

private static string GetStringAndReleaseBuilder(StringBuilder builder)
{
string result = builder.ToString();
if (builder.Capacity <= MaxCachedBuilderCapacity)
{
s_cachedBuilder = builder;
}

return result;
}

/// <summary>
/// Appends a collection of objects using their display-name representation.
/// </summary>
private static void AppendHumanizedArguments(StringBuilder builder, object?[] data)
{
for (int i = 0; i < data.Length; i++)
{
if (i > 0)
{
builder.Append(',');
}

AppendHumanizedArgument(builder, data[i]);
}
}

/// <summary>
Expand Down Expand Up @@ -73,6 +141,12 @@ private static void AppendHumanizedArgument(StringBuilder builder, object? data)
builder.Append('\'').Append(value).Append('\'');
break;

case object?[] values:
builder.Append('[');
AppendHumanizedArguments(builder, values);
builder.Append(']');
break;

case Array:
builder.Append('[');
AppendHumanizedArguments(builder, (IEnumerable)data);
Expand All @@ -84,4 +158,15 @@ private static void AppendHumanizedArgument(StringBuilder builder, object? data)
break;
}
}

private sealed class MethodData
{
public MethodData(MethodInfo method)
{
ParameterInfo[] parameters = method.GetParameters();
HasSingleObjectArrayParameter = parameters.Length == 1 && parameters[0].ParameterType == typeof(object[]);
}

public bool HasSingleObjectArrayParameter { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,26 @@ public void GetDisplayNameCapturesLocalizedFormatBeforeFormattingValues()
}
}

public void GetDisplayNameRefreshesLocalizedFormatWhenUICultureChanges()
{
CultureInfo previousUICulture = CultureInfo.CurrentUICulture;
try
{
MethodInfo methodInfo = typeof(DummyTestClass).GetMethod(nameof(DummyTestClass.DataRowTestMethod))!;
var attribute = new DataRowAttribute();

CultureInfo.CurrentUICulture = new CultureInfo("en-US");
attribute.GetDisplayName(methodInfo, ["value"]).Should().Be("DataRowTestMethod (\"value\")");

CultureInfo.CurrentUICulture = new CultureInfo("ko-KR");
attribute.GetDisplayName(methodInfo, ["value"]).Should().Be("DataRowTestMethod(\"value\")");
}
finally
{
CultureInfo.CurrentUICulture = previousUICulture;
}
}

private class DummyDataRowAttribute : DataRowAttribute
{
public override string GetDisplayName(MethodInfo methodInfo, object?[]? data) => "Overridden DisplayName";
Expand Down