Skip to content

Commit

Permalink
Convert parameterized tests to use ArgumentConstraintTestCases
Browse files Browse the repository at this point in the history
  • Loading branch information
blairconrad committed Mar 15, 2021
1 parent 0d95f00 commit 319f26a
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 162 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
namespace FakeItEasy.Analyzer.CSharp.Tests
{
using System.Collections.Generic;
using FakeItEasy.Analyzer.Tests.Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Xunit;

public class ArgumentConstraintOutsideCallSpecAnalyzerTests : DiagnosticVerifier
{
public static IEnumerable<object?[]> Constraints =>
TestCases.FromObject(
public static TheoryData<string> Constraints =>
ArgumentConstraintTestCases.From(
"A<int>._",
"A<int>.Ignored",
"A<int>.That.IsEqualTo(42)",
"A<int>.That.Not.IsEqualTo(42)");

public static TheoryData<string> EvenIncompleteConstraints =>
ArgumentConstraintTestCases.From(
"A<int>._",
"A<int>.Ignored",
"A<int>.That",
"A<int>.That.Not",
"A<int>.That.IsEqualTo(42)",
"A<int>.That.Not.IsEqualTo(42)");

public static TheoryData<string> ErroneousConstraints =>
ArgumentConstraintTestCases.From(
"A<int>.That.Matches(x => Test(x))");

[Theory]
[InlineData("A<int>._")]
[InlineData("A<int>.Ignored")]
[InlineData("A<int>.That")]
[InlineData("A<int>.That.Not")]
[InlineData("A<int>.That.IsEqualTo(42)")]
[InlineData("A<int>.That.Not.IsEqualTo(42)")]
[MemberData(nameof(EvenIncompleteConstraints))]
public void Diagnostic_should_be_triggered_for_constraint_assigned_to_variable(string constraint)
{
string code = $@"using FakeItEasy;
Expand Down Expand Up @@ -217,56 +224,58 @@ interface IFoo {{ int Bar {{ get; set; }} }}
this.VerifyCSharpDiagnostic(code);
}

[Fact]
public void Diagnostic_should_not_be_triggered_if_constraint_inside_call_spec_contains_error()
[Theory]
[MemberData(nameof(ErroneousConstraints))]
public void Diagnostic_should_not_be_triggered_if_constraint_inside_call_spec_contains_error(string constraint)
{
string code = @"using FakeItEasy;
string code = $@"using FakeItEasy;
namespace TheNamespace
{
{{
class TheClass
{
{{
void Test()
{
{{
var foo = A.Fake<IFoo>();
A.CallTo(() => foo.Bar(A<int>.That.Matches(x => Test(x)))).Returns(42);
}
A.CallTo(() => foo.Bar({constraint})).Returns(42);
}}
bool Test(byte x) => true;
}
}}
interface IFoo { void Bar(int x); }
}
interface IFoo {{ void Bar(int x); }}
}}
";

this.VerifyCSharpDiagnosticWithCompilationErrors(code);
}

[Fact]
public void Diagnostic_should_be_triggered_if_constraint_outside_call_spec_contains_error()
[Theory]
[MemberData(nameof(ErroneousConstraints))]
public void Diagnostic_should_be_triggered_if_constraint_outside_call_spec_contains_error(string constraint)
{
string code = @"using FakeItEasy;
string code = $@"using FakeItEasy;
namespace TheNamespace
{
{{
class TheClass
{
{{
void Test()
{
var c = A<int>.That.Matches(x => Test(x));
}
{{
var c = {constraint};
}}
bool Test(byte x) => true;
}
}}
interface IFoo { void Bar(int x); }
}
interface IFoo {{ void Bar(int x); }}
}}
";

this.VerifyCSharpDiagnosticWithCompilationErrors(
code,
new DiagnosticResult
{
Id = "FakeItEasy0003",
Message = "Argument constraint 'A<int>.That.Matches(x => Test(x))' is not valid outside a call specification.",
Message = $"Argument constraint '{constraint}' is not valid outside a call specification.",
Severity = DiagnosticSeverity.Warning,
Locations = new[] { new DiagnosticResultLocation("Test0.cs", 8, 21) }
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace FakeItEasy.Analyzer.CSharp.Tests
{
using System.Collections.Generic;
using System;
using FakeItEasy.Analyzer.Tests.Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
Expand Down Expand Up @@ -54,24 +54,24 @@ public static implicit operator CanBeConvertedTo(CanBeConvertedFrom x)
}}
}}";

public static IEnumerable<object?[]> FakeItEasy0004SupportedConstraints =>
TestCases.FromObject(
"_",
"Ignored");
public static TheoryData<string> FakeItEasy0004SupportedConstraints =>
ArgumentConstraintTestCases.From(
"A<{0}>._",
"A<{0}>.Ignored");

public static IEnumerable<object?[]> FakeItEasy0005SupportedConstraints =>
TestCases.FromObject(
"_",
"Ignored",
"That.Matches(_ => true)");
public static TheoryData<string> FakeItEasy0005SupportedConstraints =>
ArgumentConstraintTestCases.From(
"A<{0}>._",
"A<{0}>.Ignored",
"A<{0}>.That.Matches(_ => true)");

public static IEnumerable<object?[]> AllSupportedConstraints => FakeItEasy0005SupportedConstraints;
public static TheoryData<string> AllSupportedConstraints => FakeItEasy0005SupportedConstraints;

[Theory]
[MemberData(nameof(AllSupportedConstraints))]
public void No_diagnostic_should_be_triggered_when_nullable_constraint_is_used_for_nullable_parameter(string constraint)
{
string completeConstraint = $"A<int?>.{constraint}";
string completeConstraint = string.Format(constraint, "int?");
string call = $"foo.NullableIntParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

Expand All @@ -82,7 +82,7 @@ public void No_diagnostic_should_be_triggered_when_nullable_constraint_is_used_f
[MemberData(nameof(AllSupportedConstraints))]
public void No_diagnostic_should_be_triggered_when_non_nullable_constraint_is_used_for_non_nullable_parameter(string constraint)
{
string completeConstraint = $"A<int>.{constraint}";
string completeConstraint = string.Format(constraint, "int");
string call = $"foo.NonNullableIntParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

Expand All @@ -93,7 +93,7 @@ public void No_diagnostic_should_be_triggered_when_non_nullable_constraint_is_us
[MemberData(nameof(AllSupportedConstraints))]
public void No_diagnostic_should_be_triggered_when_derived_nonnullable_class_constraint_is_used_with_base_class_parameter(string constraint)
{
string completeConstraint = $"A<DerivedClass>.{constraint}";
string completeConstraint = string.Format(constraint, "DerivedClass");
string call = $"foo.BaseClassParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

Expand All @@ -104,7 +104,7 @@ public void No_diagnostic_should_be_triggered_when_derived_nonnullable_class_con
[MemberData(nameof(FakeItEasy0004SupportedConstraints))]
public void FakeItEasy0004_should_be_triggered_when_non_nullable_constraint_is_used_for_nullable_parameter(string constraint)
{
string completeConstraint = $"A<int>.{constraint}";
string completeConstraint = string.Format(constraint, "int");
string call = $"foo.NullableIntParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

Expand All @@ -123,7 +123,7 @@ public void FakeItEasy0004_should_be_triggered_when_non_nullable_constraint_is_u
[MemberData(nameof(FakeItEasy0004SupportedConstraints))]
public void FakeItEasy0004_should_be_triggered_when_non_nullable_constraint_is_used_for_nullable_parameter_for_indexer(string constraint)
{
string completeConstraint = $"A<int>.{constraint}";
string completeConstraint = string.Format(constraint, "int");
string call = $"foo[{completeConstraint}]";
string code = string.Format(CodeTemplate, call);

Expand All @@ -142,7 +142,7 @@ public void FakeItEasy0004_should_be_triggered_when_non_nullable_constraint_is_u
[MemberData(nameof(FakeItEasy0005SupportedConstraints))]
public void FakeItEasy0005_should_be_triggered_when_wrong_typed_nonnullable_constraint_is_used_with_nonnullable_parameter(string constraint)
{
string completeConstraint = $"A<int>.{constraint}";
string completeConstraint = string.Format(constraint, "int");
string call = $"foo.NonNullableDoubleParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

Expand All @@ -161,7 +161,7 @@ public void FakeItEasy0005_should_be_triggered_when_wrong_typed_nonnullable_cons
[MemberData(nameof(FakeItEasy0005SupportedConstraints))]
public void FakeItEasy0005_should_be_triggered_when_wrong_typed_nullable_constraint_is_used_with_nullable_parameter(string constraint)
{
string completeConstraint = $"A<int?>.{constraint}";
string completeConstraint = string.Format(constraint, "int?");
string call = $"foo.NullableLongParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

Expand All @@ -180,7 +180,7 @@ public void FakeItEasy0005_should_be_triggered_when_wrong_typed_nullable_constra
[MemberData(nameof(FakeItEasy0005SupportedConstraints))]
public void FakeItEasy0005_should_be_triggered_when_constraint_with_implicit_conversion_is_used_with_target_type(string constraint)
{
string completeConstraint = $"A<CanBeConvertedFrom>.{constraint}";
string completeConstraint = string.Format(constraint, "CanBeConvertedFrom");
string call = $"foo.HasImplicitConversionParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

Expand All @@ -199,11 +199,11 @@ public void FakeItEasy0005_should_be_triggered_when_constraint_with_implicit_con
[MemberData(nameof(FakeItEasy0004SupportedConstraints))]
public void MakeConstraintNullable_CodeFix_should_replace_constraint_with_nullable_constraint(string constraint)
{
string completeConstraint = $"A<int>.{constraint}";
string completeConstraint = string.Format(constraint, "int");
string call = $"foo.NullableIntParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

string fixedConstraint = $"A<int?>.{constraint}";
string fixedConstraint = string.Format(constraint, "int?");
string fixedCall = $"foo.NullableIntParam({fixedConstraint})";
string fixedCode = string.Format(CodeTemplate, fixedCall);
this.VerifyCSharpFix(code, fixedCode, codeFixIndex: 0);
Expand All @@ -213,11 +213,11 @@ public void MakeConstraintNullable_CodeFix_should_replace_constraint_with_nullab
[MemberData(nameof(FakeItEasy0004SupportedConstraints))]
public void MakeConstraintNullable_CodeFix_should_replace_constraint_with_nullable_constraint_for_indexer(string constraint)
{
string completeConstraint = $"A<int>.{constraint}";
string completeConstraint = string.Format(constraint, "int");
string call = $"foo[{completeConstraint}]";
string code = string.Format(CodeTemplate, call);

string fixedConstraint = $"A<int?>.{constraint}";
string fixedConstraint = string.Format(constraint, "int?");
string fixedCall = $"foo[{fixedConstraint}]";
string fixedCode = string.Format(CodeTemplate, fixedCall);
this.VerifyCSharpFix(code, fixedCode, codeFixIndex: 0);
Expand All @@ -227,11 +227,11 @@ public void MakeConstraintNullable_CodeFix_should_replace_constraint_with_nullab
[MemberData(nameof(FakeItEasy0004SupportedConstraints))]
public void MakeNotNullConstraint_CodeFix_should_replace_constraint_with_AThatIsNotNull(string constraint)
{
string completeConstraint = $"A<int>.{constraint}";
string completeConstraint = string.Format(constraint, "int");
string call = $"foo.NullableIntParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

string fixedConstraint = "A<int?>.That.IsNotNull()";
string fixedConstraint = GetArgumentConstraintEntry(constraint) + "<int?>.That.IsNotNull()";
string fixedCall = $"foo.NullableIntParam({fixedConstraint})";
string fixedCode = string.Format(CodeTemplate, fixedCall);
this.VerifyCSharpFix(code, fixedCode, codeFixIndex: 1);
Expand All @@ -241,11 +241,11 @@ public void MakeNotNullConstraint_CodeFix_should_replace_constraint_with_AThatIs
[MemberData(nameof(FakeItEasy0004SupportedConstraints))]
public void MakeNotNullConstraint_CodeFix_should_replace_constraint_with_AThatIsNotNull_for_indexer(string constraint)
{
string completeConstraint = $"A<int>.{constraint}";
string completeConstraint = string.Format(constraint, "int");
string call = $"foo[{completeConstraint}]";
string code = string.Format(CodeTemplate, call);

string fixedConstraint = "A<int?>.That.IsNotNull()";
string fixedConstraint = GetArgumentConstraintEntry(constraint) + "<int?>.That.IsNotNull()";
string fixedCall = $"foo[{fixedConstraint}]";
string fixedCode = string.Format(CodeTemplate, fixedCall);
this.VerifyCSharpFix(code, fixedCode, codeFixIndex: 1);
Expand All @@ -255,11 +255,11 @@ public void MakeNotNullConstraint_CodeFix_should_replace_constraint_with_AThatIs
[MemberData(nameof(FakeItEasy0005SupportedConstraints))]
public void ChangeConstraintType_CodeFix_should_replace_constraint_with_proper_type(string constraint)
{
string completeConstraint = $"A<short>.{constraint}";
string completeConstraint = string.Format(constraint, "short");
string call = $"foo.NonNullableDoubleParam({completeConstraint})";
string code = string.Format(CodeTemplate, call);

string fixedConstraint = $"A<double>.{constraint}";
string fixedConstraint = string.Format(constraint, "double");
string fixedCall = $"foo.NonNullableDoubleParam({fixedConstraint})";
string fixedCode = string.Format(CodeTemplate, fixedCall);
this.VerifyCSharpFix(code, fixedCode, codeFixIndex: 0);
Expand All @@ -269,11 +269,11 @@ public void ChangeConstraintType_CodeFix_should_replace_constraint_with_proper_t
[MemberData(nameof(FakeItEasy0005SupportedConstraints))]
public void ChangeConstraintType_CodeFix_should_replace_constraint_with_proper_type_for_indexer(string constraint)
{
string completeConstraint = $"A<short>.{constraint}";
string completeConstraint = string.Format(constraint, "short");
string call = $"foo[{completeConstraint}, \"hello\"]";
string code = string.Format(CodeTemplate, call);

string fixedConstraint = $"A<double>.{constraint}";
string fixedConstraint = string.Format(constraint, "double");
string fixedCall = $"foo[{fixedConstraint}, \"hello\"]";
string fixedCode = string.Format(CodeTemplate, fixedCall);
this.VerifyCSharpFix(code, fixedCode, codeFixIndex: 0);
Expand All @@ -288,5 +288,12 @@ protected override CodeFixProvider GetCSharpCodeFixProvider()
{
return new ArgumentConstraintTypeMismatchCodeFixProvider();
}

private static string GetArgumentConstraintEntry(string argumentConstraint)
{
// Sometimes an argument constraint will being with A and sometimes An,
// and we need to find out which.
return argumentConstraint.Substring(0, argumentConstraint.IndexOf("<", StringComparison.Ordinal));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace FakeItEasy.Analyzer.Tests.Helpers
{
using System;
using Xunit;

public static class ArgumentConstraintTestCases
{
/// <summary>
/// Create test cases from the given argument constraints.
/// </summary>
/// <param newm="constraints">
/// List of constraints to use as a seed. Each constraint must use the "A"
/// argument constraint-building entry point. Test cases using "An" will
/// be built to augment these.
/// </param>
public static TheoryData<string> From(params string[] constraints)
{
var theoryData = new TheoryData<string>();
foreach (var constraint in constraints)
{
ValidateConstraint(constraint);

theoryData.Add(constraint);
}

return theoryData;
}

private static void ValidateConstraint(string constraint)
{
if (constraint.Length < 2 ||
constraint[0] != 'A' ||
(constraint[1] != '<' && constraint[1] != '('))
{
throw new ArgumentException(
$"Constraint '{constraint}' is not an argument constraint built using the 'A' entry point.",
nameof(constraint));
}
}
}
}
Loading

0 comments on commit 319f26a

Please sign in to comment.