Skip to content

Commit

Permalink
better visibility for mstest discovery (#253)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meir017 authored Dec 13, 2023
1 parent dd3a22e commit 8959d44
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 280 deletions.
162 changes: 72 additions & 90 deletions src/FluentAssertions.Analyzers.Tests/TestAttributes.cs
Original file line number Diff line number Diff line change
@@ -1,117 +1,99 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace FluentAssertions.Analyzers.Tests
namespace FluentAssertions.Analyzers.Tests;

[AttributeUsage(AttributeTargets.Method)]
public class NotImplementedAttribute : TestCategoryBaseAttribute
{
[AttributeUsage(AttributeTargets.Method)]
public class NotImplementedAttribute : TestCategoryBaseAttribute
{
public string Reason { get; set; }

public override IList<string> TestCategories => new[] { "NotImplemented" };
}
public string Reason { get; set; }

[AttributeUsage(AttributeTargets.Method)]
public class ImplementedAttribute : TestCategoryBaseAttribute
{
public string Reason { get; set; }
public override IList<string> TestCategories => new[] { "NotImplemented" };
}

public override IList<string> TestCategories => new[] { "Completed" };
}
[AttributeUsage(AttributeTargets.Method)]
public class ImplementedAttribute : TestCategoryBaseAttribute
{
public string Reason { get; set; }

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class AssertionDiagnosticAttribute : Attribute
{
public string Assertion { get; }
public override IList<string> TestCategories => new[] { "Completed" };
}

public bool Ignore { get; }
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class AssertionDiagnosticAttribute : Attribute, ITestDataSource
{
public string Assertion { get; }

public AssertionDiagnosticAttribute(string assertion, bool ignore = false)
{
Assertion = assertion;
Ignore = ignore;
}
}
public bool Ignore { get; }

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class AssertionCodeFixAttribute : Attribute
{
public string OldAssertion { get; }
public string NewAssertion { get; }
public AssertionDiagnosticAttribute(string assertion, bool ignore = false) => (Assertion, Ignore) = (assertion, ignore);

public bool Ignore { get; }
public IEnumerable<object[]> GetData(MethodInfo methodInfo)
{
if (Ignore) yield break;

public AssertionCodeFixAttribute(string oldAssertion, string newAssertion, bool ignore = false)
foreach (var assertion in TestCasesInputUtils.GetTestCases(Assertion))
{
OldAssertion = oldAssertion;
NewAssertion = newAssertion;
Ignore = ignore;
yield return new object[] { assertion };
}
}

[AttributeUsage(AttributeTargets.Method)]
public class AssertionDataTestMethod : TestMethodAttribute
public string GetDisplayName(MethodInfo methodInfo, object[] data) => $"{methodInfo.Name}(\"{data[0]}\")";
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class AssertionCodeFixAttribute : Attribute, ITestDataSource
{
public string OldAssertion { get; }
public string NewAssertion { get; }

public bool Ignore { get; }

public AssertionCodeFixAttribute(string oldAssertion, string newAssertion, bool ignore = false) => (OldAssertion, NewAssertion, Ignore) = (oldAssertion, newAssertion, ignore);

public IEnumerable<object[]> GetData(MethodInfo methodInfo)
{
private static readonly string Empty = string.Empty;
private static readonly string Because = "\"because it's possible\"";
private static readonly string FormattedBecause = "\"because message with {0} placeholders {1} at {2}\", 3, \"is awesome\", DateTime.Now.Add(2.Seconds())";
if (Ignore) yield break;

public override TestResult[] Execute(ITestMethod testMethod)
foreach (var (oldAssertion, newAssertion) in TestCasesInputUtils.GetTestCases(OldAssertion, NewAssertion))
{
var diagnosticAttributes = testMethod.GetAttributes<AssertionDiagnosticAttribute>(false);
var codeFixAttributes = testMethod.GetAttributes<AssertionCodeFixAttribute>(false);

var results = new List<TestResult>();
foreach (var diagnosticAttribute in diagnosticAttributes.Where(attribute => !attribute.Ignore))
{
foreach (var assertion in GetTestCases(diagnosticAttribute))
{
var result = testMethod.Invoke(new[] { assertion });
result.DisplayName = $"{testMethod.TestMethodName} (\"{assertion}\")";

results.Add(result);
}
}
foreach (var codeFixAttribute in codeFixAttributes.Where(attribute => !attribute.Ignore))
{
foreach (var (oldAssertion, newAssertion) in GetTestCases(codeFixAttribute))
{
var result = testMethod.Invoke(new[] { oldAssertion, newAssertion });
result.DisplayName = $"{testMethod.TestMethodName} ({Environment.NewLine}old: \"{oldAssertion}\" {Environment.NewLine}new: \"{newAssertion}\")";

results.Add(result);
}
}

return results.ToArray();
yield return new object[] { oldAssertion, newAssertion };
}
}

private IEnumerable<string> GetTestCases(AssertionDiagnosticAttribute attribute)
{
yield return SafeFormat(attribute.Assertion, Empty);
yield return SafeFormat(attribute.Assertion, Because);
yield return SafeFormat(attribute.Assertion, FormattedBecause);
}
private IEnumerable<(string oldAssertion, string newAssertion)> GetTestCases(AssertionCodeFixAttribute attribute)
{
yield return (SafeFormat(attribute.OldAssertion, Empty), SafeFormat(attribute.NewAssertion, Empty));
yield return (SafeFormat(attribute.OldAssertion, Because), SafeFormat(attribute.NewAssertion, Because));
yield return (SafeFormat(attribute.OldAssertion, FormattedBecause), SafeFormat(attribute.NewAssertion, FormattedBecause));
}
public string GetDisplayName(MethodInfo methodInfo, object[] data) => $"{methodInfo.Name}(\"old: {data[0]}\", new: {data[1]}\")";
}

public static class TestCasesInputUtils
{
private static readonly string Empty = string.Empty;
private static readonly string Because = "\"because it's possible\"";
private static readonly string FormattedBecause = "\"because message with {0} placeholders {1} at {2}\", 3, \"is awesome\", DateTime.Now.Add(2.Seconds())";
public static IEnumerable<string> GetTestCases(string assertion)
{
yield return SafeFormat(assertion, Empty);
yield return SafeFormat(assertion, Because);
yield return SafeFormat(assertion, FormattedBecause);
}
public static IEnumerable<(string oldAssertion, string newAssertion)> GetTestCases(string oldAssertion, string newAssertion)
{
yield return (SafeFormat(oldAssertion, Empty), SafeFormat(newAssertion, Empty));
yield return (SafeFormat(oldAssertion, Because), SafeFormat(newAssertion, Because));
yield return (SafeFormat(oldAssertion, FormattedBecause), SafeFormat(newAssertion, FormattedBecause));
}

/// <summary>
/// Adds an comma before the additional argument if needed.
/// </summary>
private string SafeFormat(string assertion, string arg)
/// <summary>
/// Adds an comma before the additional argument if needed.
/// </summary>
private static string SafeFormat(string assertion, string arg)
{
var index = assertion.IndexOf("{0}");
if (!string.IsNullOrEmpty(arg) && assertion[index - 1] != '(')
{
var index = assertion.IndexOf("{0}");
if (!string.IsNullOrEmpty(arg) && assertion[index - 1] != '(')
{
return string.Format(assertion, ", " + arg);
}
return string.Format(assertion, arg);
return string.Format(assertion, ", " + arg);
}
return string.Format(assertion, arg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void AssignAsyncVoidLambdaToAction_TestAnalyzer(string assertion)
[AssertionCodeFix(
oldAssertion: "Action action1 = async () => { await Task.CompletedTask; }, action2 = async () => { await Task.CompletedTask; };",
newAssertion: "Func<Task> action1 = async () => { await Task.CompletedTask; }, action2 = async () => { await Task.CompletedTask; };")]
[AssertionDataTestMethod]
[DataTestMethod]
[NotImplemented]
public void AssignAsyncVoidLambdaToAction_TestCodeFix(string oldAssertion, string newAssertion)
{
Expand Down
Loading

0 comments on commit 8959d44

Please sign in to comment.