Skip to content

Commit

Permalink
Update xUnit3000 to support v3's IXunitSerializable
Browse files Browse the repository at this point in the history
  • Loading branch information
bradwilson committed Jul 6, 2024
1 parent fec8d66 commit db1f0c2
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ static async Task<Document> CreateOrUpdateConstructor(
var newCtor = generator.ConstructorDeclaration();
newCtor = generator.WithAccessibility(newCtor, Accessibility.Public);
newCtor = generator.AddAttributes(newCtor, obsoleteAttribute);
editor.InsertMembers(declaration, 0, new[] { newCtor });
editor.InsertMembers(declaration, 0, [newCtor]);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
using Microsoft.CodeAnalysis;
using Xunit;
using Xunit.Analyzers;
using Verify = CSharpVerifier<SerializableClassMustHaveParameterlessConstructorTests.Analyzer>;
using VerifyV2 = CSharpVerifier<SerializableClassMustHaveParameterlessConstructorTests.V2Analyzer>;
using VerifyV3 = CSharpVerifier<SerializableClassMustHaveParameterlessConstructorTests.V3Analyzer>;

public class SerializableClassMustHaveParameterlessConstructorTests
{
static readonly string Template = @"
using Xunit.Abstractions;
using {2};
public interface IMySerializer : IXunitSerializable {{ }}
public class Foo : {0}
Expand All @@ -26,51 +27,83 @@ public void Serialize(IXunitSerializationInfo info) {{ }}
[MemberData(nameof(Interfaces))]
public async Task ImplicitConstructors_NoDiagnostics(string @interface)
{
var source = string.Format(Template, @interface, "");
var v2Source = string.Format(Template, @interface, "", "Xunit.Abstractions");

await Verify.VerifyAnalyzerV2(source);
await VerifyV2.VerifyAnalyzerV2(v2Source);

var v3Source = string.Format(Template, @interface, "", "Xunit.Sdk");

await VerifyV3.VerifyAnalyzerV3(v3Source);
}

[Theory]
[MemberData(nameof(Interfaces))]
public async Task WrongConstructor_ReturnsError(string @interface)
{
var source = string.Format(Template, @interface, "public Foo(int x) { }");
var expected =
Verify
var v2Source = string.Format(Template, @interface, "public Foo(int x) { }", "Xunit.Abstractions");
var v2Expected =
VerifyV2
.Diagnostic()
.WithSpan(5, 14, 5, 17)
.WithArguments("Foo");

await Verify.VerifyAnalyzerV2(source, expected);
await VerifyV2.VerifyAnalyzerV2(v2Source, v2Expected);

var v3Source = string.Format(Template, @interface, "public Foo(int x) { }", "Xunit.Sdk");
var v3Expected =
VerifyV3
.Diagnostic()
.WithSpan(5, 14, 5, 17)
.WithArguments("Foo");

await VerifyV3.VerifyAnalyzerV3(v3Source, v3Expected);
}

[Theory]
[MemberData(nameof(Interfaces))]
public async Task NonPublicConstructor_ReturnsError(string @interface)
{
var source = string.Format(Template, @interface, "protected Foo() { }");
var expected =
Verify
var v2Source = string.Format(Template, @interface, "protected Foo() { }", "Xunit.Abstractions");
var v2Expected =
VerifyV2
.Diagnostic()
.WithSpan(5, 14, 5, 17)
.WithArguments("Foo");

await VerifyV2.VerifyAnalyzerV2(v2Source, v2Expected);

var v3Source = string.Format(Template, @interface, "protected Foo() { }", "Xunit.Sdk");
var v3Expected =
VerifyV3
.Diagnostic()
.WithSpan(5, 14, 5, 17)
.WithArguments("Foo");

await Verify.VerifyAnalyzerV2(source, expected);
await VerifyV3.VerifyAnalyzerV3(v3Source, v3Expected);
}

[Theory]
[MemberData(nameof(Interfaces))]
public async Task PublicParameterlessConstructor_NoDiagnostics(string @interface)
{
var source = string.Format(Template, @interface, "public Foo() { }");
var v2Source = string.Format(Template, @interface, "public Foo() { }", "Xunit.Abstractions");

await VerifyV2.VerifyAnalyzerV2(v2Source);

var v3Source = string.Format(Template, @interface, "public Foo() { }", "Xunit.Sdk");

await Verify.VerifyAnalyzerV2(source);
await VerifyV3.VerifyAnalyzerV3(v3Source);
}

public class Analyzer : SerializableClassMustHaveParameterlessConstructor
public class V2Analyzer : SerializableClassMustHaveParameterlessConstructor
{
protected override XunitContext CreateXunitContext(Compilation compilation) =>
XunitContext.ForV2Abstractions(compilation);
}

public class V3Analyzer : SerializableClassMustHaveParameterlessConstructor
{
protected override XunitContext CreateXunitContext(Compilation compilation) =>
XunitContext.ForV3Core(compilation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,109 +8,145 @@ public class SerializableClassMustHaveParameterlessConstructorFixerTests
[Fact]
public async Task WithPublicParameteredConstructor_AddsNewConstructor()
{
var before = @"
public class [|MyTestCase|]: Xunit.Abstractions.IXunitSerializable {
public MyTestCase(int x) { }
var beforeTemplate = @"
public class [|MyTestCase|]: {0}.IXunitSerializable {{
public MyTestCase(int x) {{ }}
void Xunit.Abstractions.IXunitSerializable.Deserialize(Xunit.Abstractions.IXunitSerializationInfo _) { }
void Xunit.Abstractions.IXunitSerializable.Serialize(Xunit.Abstractions.IXunitSerializationInfo _) { }
}";
void {0}.IXunitSerializable.Deserialize({0}.IXunitSerializationInfo _) {{ }}
void {0}.IXunitSerializable.Serialize({0}.IXunitSerializationInfo _) {{ }}
}}";

var after = @"
public class MyTestCase: Xunit.Abstractions.IXunitSerializable {
var afterTemplate = @"
public class MyTestCase: {0}.IXunitSerializable {{
[System.Obsolete(""Called by the de-serializer; should only be called by deriving classes for de-serialization purposes"")]
public MyTestCase()
{
}
{{
}}
public MyTestCase(int x) { }
public MyTestCase(int x) {{ }}
void Xunit.Abstractions.IXunitSerializable.Deserialize(Xunit.Abstractions.IXunitSerializationInfo _) { }
void Xunit.Abstractions.IXunitSerializable.Serialize(Xunit.Abstractions.IXunitSerializationInfo _) { }
}";
void {0}.IXunitSerializable.Deserialize({0}.IXunitSerializationInfo _) {{ }}
void {0}.IXunitSerializable.Serialize({0}.IXunitSerializationInfo _) {{ }}
}}";

await Verify.VerifyCodeFixV2(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);
var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions");
var v2After = string.Format(afterTemplate, "Xunit.Abstractions");

await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);

var v3Before = string.Format(beforeTemplate, "Xunit.Sdk");
var v3After = string.Format(afterTemplate, "Xunit.Sdk");

await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);
}

[Fact]
public async Task WithNonPublicParameterlessConstructor_ChangesVisibility_WithoutUsing()
{
var before = @"
public class [|MyTestCase|]: Xunit.Abstractions.IXunitSerializable {
protected MyTestCase() { throw new System.DivideByZeroException(); }
var beforeTemplate = @"
using {0};
public class [|MyTestCase|]: IXunitSerializable {{
protected MyTestCase() {{ throw new System.DivideByZeroException(); }}
void Xunit.Abstractions.IXunitSerializable.Deserialize(Xunit.Abstractions.IXunitSerializationInfo _) { }
void Xunit.Abstractions.IXunitSerializable.Serialize(Xunit.Abstractions.IXunitSerializationInfo _) { }
}";
void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }}
void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }}
}}";

var after = @"
public class MyTestCase: Xunit.Abstractions.IXunitSerializable {
var afterTemplate = @"
using {0};
public class MyTestCase: IXunitSerializable {{
[System.Obsolete(""Called by the de-serializer; should only be called by deriving classes for de-serialization purposes"")]
public MyTestCase() { throw new System.DivideByZeroException(); }
public MyTestCase() {{ throw new System.DivideByZeroException(); }}
void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }}
void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }}
}}";

var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions");
var v2After = string.Format(afterTemplate, "Xunit.Abstractions");

void Xunit.Abstractions.IXunitSerializable.Deserialize(Xunit.Abstractions.IXunitSerializationInfo _) { }
void Xunit.Abstractions.IXunitSerializable.Serialize(Xunit.Abstractions.IXunitSerializationInfo _) { }
}";
await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);

await Verify.VerifyCodeFixV2(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);
var v3Before = string.Format(beforeTemplate, "Xunit.Sdk");
var v3After = string.Format(afterTemplate, "Xunit.Sdk");

await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);
}

[Fact]
public async Task WithNonPublicParameterlessConstructor_ChangesVisibility_WithUsing()
{
var before = @"
var beforeTemplate = @"
using System;
using Xunit.Abstractions;
using {0};
public class [|MyTestCase|]: IXunitSerializable {
protected MyTestCase() { throw new DivideByZeroException(); }
public class [|MyTestCase|]: IXunitSerializable {{
protected MyTestCase() {{ throw new DivideByZeroException(); }}
void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { }
void IXunitSerializable.Serialize(IXunitSerializationInfo _) { }
}";
void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }}
void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }}
}}";

var after = @"
var afterTemplate = @"
using System;
using Xunit.Abstractions;
using {0};
public class MyTestCase: IXunitSerializable {
public class MyTestCase: IXunitSerializable {{
[Obsolete(""Called by the de-serializer; should only be called by deriving classes for de-serialization purposes"")]
public MyTestCase() { throw new DivideByZeroException(); }
public MyTestCase() {{ throw new DivideByZeroException(); }}
void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }}
void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }}
}}";

var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions");
var v2After = string.Format(afterTemplate, "Xunit.Abstractions");

await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);

void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { }
void IXunitSerializable.Serialize(IXunitSerializationInfo _) { }
}";
var v3Before = string.Format(beforeTemplate, "Xunit.Sdk");
var v3After = string.Format(afterTemplate, "Xunit.Sdk");

await Verify.VerifyCodeFixV2(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);
await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);
}

[Fact]
public async Task PreservesExistingObsoleteAttribute()
{
var before = @"
using Xunit.Abstractions;
var beforeTemplate = @"
using {0};
using obo = System.ObsoleteAttribute;
public class [|MyTestCase|]: IXunitSerializable {
public class [|MyTestCase|]: IXunitSerializable {{
[obo(""This is my custom obsolete message"")]
protected MyTestCase() { throw new System.DivideByZeroException(); }
protected MyTestCase() {{ throw new System.DivideByZeroException(); }}
void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { }
void IXunitSerializable.Serialize(IXunitSerializationInfo _) { }
}";
void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }}
void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }}
}}";

var after = @"
using Xunit.Abstractions;
var afterTemplate = @"
using {0};
using obo = System.ObsoleteAttribute;
public class MyTestCase: IXunitSerializable {
public class MyTestCase: IXunitSerializable {{
[obo(""This is my custom obsolete message"")]
public MyTestCase() { throw new System.DivideByZeroException(); }
public MyTestCase() {{ throw new System.DivideByZeroException(); }}
void IXunitSerializable.Deserialize(IXunitSerializationInfo _) {{ }}
void IXunitSerializable.Serialize(IXunitSerializationInfo _) {{ }}
}}";

var v2Before = string.Format(beforeTemplate, "Xunit.Abstractions");
var v2After = string.Format(afterTemplate, "Xunit.Abstractions");

await Verify.VerifyCodeFixV2(v2Before, v2After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);

void IXunitSerializable.Deserialize(IXunitSerializationInfo _) { }
void IXunitSerializable.Serialize(IXunitSerializationInfo _) { }
}";
var v3Before = string.Format(beforeTemplate, "Xunit.Sdk");
var v3After = string.Format(afterTemplate, "Xunit.Sdk");

await Verify.VerifyCodeFixV2(before, after, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);
await Verify.VerifyCodeFixV3(v3Before, v3After, SerializableClassMustHaveParameterlessConstructorFixer.Key_GenerateOrUpdateConstructor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Xunit.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class SerializableClassMustHaveParameterlessConstructor : XunitV2DiagnosticAnalyzer
public class SerializableClassMustHaveParameterlessConstructor : XunitDiagnosticAnalyzer
{
public SerializableClassMustHaveParameterlessConstructor() :
base(Descriptors.X3001_SerializableClassMustHaveParameterlessConstructor)
Expand All @@ -25,7 +25,7 @@ public override void AnalyzeCompilation(
if (namedType.TypeKind != TypeKind.Class)
return;
var isXunitSerializable = xunitContext.V2Abstractions?.IXunitSerializableType?.IsAssignableFrom(namedType) ?? false;
var isXunitSerializable = xunitContext.Abstractions.IXunitSerializableType?.IsAssignableFrom(namedType) ?? false;
if (!isXunitSerializable)
return;
Expand All @@ -42,7 +42,4 @@ public override void AnalyzeCompilation(
);
}, SymbolKind.NamedType);
}

protected override bool ShouldAnalyze(XunitContext xunitContext) =>
Guard.ArgumentNotNull(xunitContext).V2Abstractions is not null;
}

0 comments on commit db1f0c2

Please sign in to comment.