Add unit tests for first-party Analyzers
This commit is contained in:
parent
867b560a1d
commit
57b5e77712
|
@ -10,6 +10,8 @@
|
|||
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.33" />
|
||||
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.10.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
|
||||
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite.Core" Version="8.0.4" />
|
||||
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
/bin
|
||||
/obj
|
||||
/TestResults
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
config="$1"
|
||||
shift
|
||||
dotnet test -c "$config" \
|
||||
-l "junit;LogFilePath=TestResults/{assembly}.coverage.xml;MethodFormat=Class;FailureBodyFormat=Verbose" \
|
||||
-l "console;verbosity=detailed" \
|
||||
"$@"
|
|
@ -0,0 +1,61 @@
|
|||
namespace BizHawk.Tests.Analyzers;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<
|
||||
BizHawk.Analyzers.FeatureNotImplementedAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
[TestClass]
|
||||
public sealed class FeatureNotImplementedAnalyzerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfFeatureNotImplementedAttr()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
using System;
|
||||
using BizHawk.Emulation.Common;
|
||||
public static class Cases {
|
||||
[FeatureNotImplemented] private static int X => throw new NotImplementedException();
|
||||
private static int Y {
|
||||
[FeatureNotImplemented] get => throw new NotImplementedException();
|
||||
[FeatureNotImplemented] set => throw new NotImplementedException();
|
||||
}
|
||||
[FeatureNotImplemented] private static int Z()
|
||||
=> throw new NotImplementedException();
|
||||
{|BHI3300:[FeatureNotImplemented] private static int A => default;|}
|
||||
private static int B {
|
||||
{|BHI3300:[FeatureNotImplemented] get => default;|}
|
||||
{|BHI3300:[FeatureNotImplemented] set => _ = value;|}
|
||||
}
|
||||
{|BHI3300:[FeatureNotImplemented] private static int C()
|
||||
=> default;|}
|
||||
// wrong exception type, same code but different message:
|
||||
[FeatureNotImplemented] private static int D => {|BHI3300:throw new InvalidOperationException()|};
|
||||
private static int E {
|
||||
[FeatureNotImplemented] get => {|BHI3300:throw new InvalidOperationException()|};
|
||||
[FeatureNotImplemented] set => {|BHI3300:throw new InvalidOperationException()|};
|
||||
}
|
||||
[FeatureNotImplemented] private static int F()
|
||||
=> {|BHI3300:throw new InvalidOperationException()|};
|
||||
// same code but different message, since only the simplest of expected syntaxes is checked for:
|
||||
[FeatureNotImplemented] private static int G => {|BHI3300:throw (new NotImplementedException())|};
|
||||
private static int H {
|
||||
[FeatureNotImplemented] get => {|BHI3300:throw (new NotImplementedException())|};
|
||||
[FeatureNotImplemented] set => {|BHI3300:throw (new NotImplementedException())|};
|
||||
}
|
||||
[FeatureNotImplemented] private static int I()
|
||||
=> {|BHI3300:throw (new NotImplementedException())|};
|
||||
// the "wat" cases (at least the ones that are reachable in practice)
|
||||
{|BHI3300:[FeatureNotImplemented] private static int K {
|
||||
get => default;
|
||||
set => _ = value;
|
||||
}|}
|
||||
}
|
||||
namespace BizHawk.Emulation.Common {
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
|
||||
public sealed class FeatureNotImplementedAttribute: Attribute {}
|
||||
}
|
||||
""");
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
namespace BizHawk.Tests.Analyzers;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<
|
||||
BizHawk.Analyzers.FirstOrDefaultOnStructAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
[TestClass]
|
||||
public sealed class FirstOrDefaultOnStructAnalyzerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfFirstOrDefault()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
public static class Cases {
|
||||
private static string? Y()
|
||||
=> new[] { 0x80.ToString(), 0x20.ToString(), 0x40.ToString() }.FirstOrDefault(static s => s.Length > 2);
|
||||
private static string? Z()
|
||||
=> new List<int> { 0x80, 0x20, 0x40 }.Select(static n => n.ToString()).FirstOrDefault();
|
||||
private static int A()
|
||||
=> {|BHI3100:new[] { 0x80, 0x20, 0x40 }.FirstOrDefault()|};
|
||||
private static int B()
|
||||
=> {|BHI3100:new List<int> { 0x80, 0x20, 0x40 }.FirstOrDefault()|};
|
||||
private static int C()
|
||||
=> {|BHI3100:new[] { 0x80, 0x20, 0x40 }.FirstOrDefault(static n => n.ToString().Length > 2)|};
|
||||
private static int D()
|
||||
=> {|BHI3100:new List<int> { 0x80, 0x20, 0x40 }.FirstOrDefault(static n => n.ToString().Length > 2)|};
|
||||
}
|
||||
namespace BizHawk.Common.CollectionExtensions {
|
||||
public static class CollectionExtensions {} // Analyzer short-circuits if this doesn't exist, since that's where the extension lives
|
||||
}
|
||||
""");
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
namespace BizHawk.Tests.Analyzers;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<
|
||||
BizHawk.Analyzers.HawkSourceAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
[TestClass]
|
||||
public sealed class HawkSourceAnalyzerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfAnonymousClasses()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
using System.Linq;
|
||||
public static class Cases {
|
||||
private static int Z()
|
||||
=> new[] { 0x80, 0x20, 0x40 }
|
||||
.Select(static n => (N: n, StrLen: n.ToString().Length))
|
||||
.Where(static pair => pair.StrLen < 3)
|
||||
.Sum(static pair => pair.N - pair.StrLen);
|
||||
private static int A()
|
||||
=> new[] { 0x80, 0x20, 0x40 }
|
||||
.Select(static n => {|BHI1002:new { N = n, StrLen = n.ToString().Length }|})
|
||||
.Where(static pair => pair.StrLen < 3)
|
||||
.Sum(static pair => pair.N - pair.StrLen);
|
||||
}
|
||||
""");
|
||||
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfAnonymousDelegates()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
using System.Linq;
|
||||
public static class Cases {
|
||||
private static int Z()
|
||||
=> new[] { 0x80, 0x20, 0x40 }
|
||||
.Where(static n => n.ToString().Length < 3)
|
||||
.Sum();
|
||||
private static int A()
|
||||
=> new[] { 0x80, 0x20, 0x40 }
|
||||
.Where({|BHI1001:static delegate(int n) { return n.ToString().Length < 3; }|})
|
||||
.Sum();
|
||||
}
|
||||
""");
|
||||
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfDefaultSwitchBranches()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
using System;
|
||||
public static class Cases {
|
||||
private static int Y(string s) {
|
||||
switch (s) {
|
||||
case "zero":
|
||||
return 0;
|
||||
case "one":
|
||||
return 1;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
private static int Z(string s)
|
||||
=> s switch {
|
||||
"zero" => 0,
|
||||
"one" => 1,
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
private static int A(string s) {
|
||||
switch (s) {
|
||||
case "zero":
|
||||
return 0;
|
||||
case "one":
|
||||
return 1;
|
||||
default:
|
||||
throw new NotImplementedException(); //TODO checking switch blocks was never implemented in the Analyzer
|
||||
}
|
||||
}
|
||||
private static int B(string s)
|
||||
=> s switch {
|
||||
"zero" => 0,
|
||||
"one" => 1,
|
||||
_ => {|BHI1005:throw new NotImplementedException()|}
|
||||
};
|
||||
private static int C(string s)
|
||||
=> s switch {
|
||||
"zero" => 0,
|
||||
"one" => 1,
|
||||
_ => {|BHI1005:throw (new NotImplementedException())|} // same code but different message, since only the simplest of expected syntaxes is checked for
|
||||
};
|
||||
}
|
||||
""");
|
||||
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfDiscards()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
public static class Cases {
|
||||
private static void Z() {
|
||||
_ = string.Empty;
|
||||
}
|
||||
private static void A() {
|
||||
var s = string.Empty;
|
||||
{|BHI1006:_ = s|};
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfInterpolatedString()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
public static class Cases {
|
||||
private static readonly int Z = $@"{0x100}".Length;
|
||||
private static readonly int A = {|BHI1004:@$"{0x100}"|}.Length;
|
||||
}
|
||||
""");
|
||||
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfListSyntaxes()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
public static class Cases {
|
||||
private static readonly int[] Y = [ 0x80, 0x20, 0x40 ];
|
||||
private static readonly bool Z = Y is [ _, > 20, .. ];
|
||||
private static readonly int[] A = {|BHI1110:[0x80, 0x20, 0x40 ]|};
|
||||
private static readonly bool B = A is {|BHI1110:[ _, > 20, ..]|};
|
||||
private static readonly bool C = A is {|BHI1110:[_, > 20, ..]|};
|
||||
}
|
||||
""");
|
||||
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfQuerySyntax()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
using System.Linq;
|
||||
using L = System.Collections.Generic.IEnumerable<(int N, int StrLen)>;
|
||||
public static class Cases {
|
||||
private static L Z()
|
||||
=> new[] { 0x80, 0x20, 0x40 }
|
||||
.Select(static n => (N: n, StrLen: n.ToString().Length))
|
||||
.OrderBy(static pair => pair.StrLen);
|
||||
private static L A()
|
||||
=> {|BHI1003:from n in new[] { 0x80, 0x20, 0x40 }
|
||||
let pair = (N: n, StrLen: n.ToString().Length)
|
||||
orderby pair.StrLen
|
||||
select pair|};
|
||||
}
|
||||
""");
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
namespace BizHawk.Tests.Analyzers;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<
|
||||
BizHawk.Analyzers.OrderBySelfAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
[TestClass]
|
||||
public sealed class OrderBySelfAnalyzerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfOrderBy()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
using System.Linq;
|
||||
using L = System.Collections.Generic.IEnumerable<int>;
|
||||
public static class Cases {
|
||||
private static readonly int[] Numbers = [ 0x80, 0x20, 0x40 ];
|
||||
private static L Y()
|
||||
=> Numbers.OrderBy(static delegate(int n) { return n.ToString().Length; });
|
||||
private static L Z()
|
||||
=> Numbers.OrderByDescending(static n => n.ToString().Length);
|
||||
private static L A()
|
||||
=> Numbers.OrderBy({|BHI3101:static delegate(int n) { return n; }|});
|
||||
private static L B()
|
||||
=> Numbers.OrderByDescending({|BHI3101:static delegate(int n) { return n; }|});
|
||||
private static L C()
|
||||
=> Numbers.OrderBy({|BHI3101:static (n) => n|});
|
||||
private static L D()
|
||||
=> Numbers.OrderByDescending({|BHI3101:static (n) => n|});
|
||||
private static L E()
|
||||
=> Numbers.OrderBy({|BHI3101:static n => n|});
|
||||
private static L F()
|
||||
=> Numbers.OrderByDescending({|BHI3101:static n => n|});
|
||||
}
|
||||
namespace BizHawk.Common.CollectionExtensions {
|
||||
public static class CollectionExtensions {} // Analyzer short-circuits if this doesn't exist, since that's where our backport lives
|
||||
}
|
||||
""");
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
namespace BizHawk.Tests.Analyzers;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<
|
||||
BizHawk.Analyzers.TryGetValueImplicitDiscardAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
[TestClass]
|
||||
public sealed class TryGetValueImplicitDiscardAnalyzerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfTryGetValue()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
using System.Collections.Generic;
|
||||
public static class Cases {
|
||||
private static Dictionary<string, int> MakeDict()
|
||||
=> new();
|
||||
private sealed class CustomDict<K, V> {
|
||||
public bool TryGetValue(K key, out V val) {
|
||||
val = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private static void Z()
|
||||
=> _ = MakeDict().TryGetValue("z", out _);
|
||||
private static void A()
|
||||
=> {|BHI1200:MakeDict().TryGetValue("a", out _)|};
|
||||
private static void B()
|
||||
=> {|BHI1200:((IDictionary<string, int>) MakeDict()).TryGetValue("b", out _)|};
|
||||
private static void C()
|
||||
=> {|BHI1200:((IReadOnlyDictionary<string, int>) MakeDict()).TryGetValue("c", out _)|};
|
||||
private static void D()
|
||||
=> {|BHI1200:new CustomDict<string, int>().TryGetValue("d", out _)|};
|
||||
}
|
||||
""");
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
namespace BizHawk.Tests.Analyzers;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<
|
||||
BizHawk.Analyzers.UseNameofOperatorAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
[TestClass]
|
||||
public sealed class UseNameofOperatorAnalyzerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfNameofOperator()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
public static class Cases {
|
||||
private static readonly int Z = typeof(Cases).FullName.Length;
|
||||
private static readonly int A = {|BHI1102:typeof(Cases).Name|}.Length;
|
||||
private static readonly int B = {|BHI1103:typeof(Cases).ToString|}().Length; // the diagnostic is added to the method group part (MemberAccessExpressionSyntax) and not the invoke part; won't matter in practice
|
||||
private static readonly int C = $"{{|BHI1103:typeof(Cases)|}}".Length;
|
||||
private static readonly int D = (">" + {|BHI1103:typeof(Cases)|}).Length;
|
||||
private static readonly int E = ({|BHI1103:typeof(Cases)|} + "<").Length;
|
||||
}
|
||||
""");
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
namespace BizHawk.Tests.Analyzers;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<
|
||||
BizHawk.Analyzers.UseTypeofOperatorAnalyzer,
|
||||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
|
||||
|
||||
[TestClass]
|
||||
public sealed class UseTypeofOperatorAnalyzerTests
|
||||
{
|
||||
[TestMethod]
|
||||
public Task CheckMisuseOfTypeofOperator()
|
||||
=> Verify.VerifyAnalyzerAsync("""
|
||||
public class Parent {
|
||||
private string Z()
|
||||
=> 3.GetType().FullName;
|
||||
private string A()
|
||||
=> {|BHI1101:this.GetType()|}.FullName;
|
||||
}
|
||||
public sealed class Child: Parent {
|
||||
private string B()
|
||||
=> {|BHI1100:this.GetType()|}.FullName;
|
||||
}
|
||||
public readonly struct Struct {
|
||||
private string C()
|
||||
=> {|BHI1100:this.GetType()|}.FullName;
|
||||
}
|
||||
""");
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<Import Project="../../Common.props" />
|
||||
<PropertyGroup>
|
||||
<EnableMSTestRunner>true</EnableMSTestRunner>
|
||||
<NoWarn>$(NoWarn);IDE0065;SA1200</NoWarn> <!-- IDE0065 and SA1200 want imports before a namespace statement -->
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" PrivateAssets="all" />
|
||||
<PackageReference Include="MSTest" PrivateAssets="all" />
|
||||
<PackageReference Include="JunitXml.TestLogger" PrivateAssets="all" />
|
||||
<ProjectReference Include="$(MSBuildProjectDirectory)/../BizHawk.Analyzer/BizHawk.Analyzer.csproj" />
|
||||
<None Remove="*.sh" />
|
||||
</ItemGroup>
|
||||
<Target Name="DontUpdatePrebuiltAssemblies" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="git restore $(MSBuildProjectDirectory)/../../References" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,3 @@
|
|||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
cd "$(dirname "$(realpath "$0")")" && ./.run_tests_with_configuration.sh "Debug" "$@"
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
cd "$(dirname "$(realpath "$0")")" && ./.run_tests_with_configuration.sh "Release" "$@"
|
Loading…
Reference in New Issue