Skip to content

Commit

Permalink
Enable razor UTs in-memory compilation (#9226)
Browse files Browse the repository at this point in the history
Co-authored-by: Cristian <67206480+CristianAmbrosini@users.noreply.github.com>
  • Loading branch information
Tim-Pohlmann and CristianAmbrosini committed May 2, 2024
1 parent 75e2d7b commit c755a17
Show file tree
Hide file tree
Showing 46 changed files with 1,705 additions and 689 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class JSInvokableMethodsShouldBePublicTest
public void JSInvokableMethodsShouldBePublic_Razor() =>
builder
.AddPaths("JSInvokableMethodsShouldBePublic.razor", "JSInvokableMethodsShouldBePublic.razor.cs")
.WithOptions(ParseOptionsHelper.FromCSharp9)
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, ProjectType.Product))
.Verify();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public class UnnecessaryUsingsTest
builder
.AddSnippet("@using System.Linq;", "_ViewImports.cshtml")
.AddSnippet(@"@using System.Text.Json; @* Noncompliant *@", fileName)
.AddReferences(NuGetMetadataReference.SystemTextJson("7.0.4"))
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, ProjectType.Product))
.Verify();

Expand All @@ -85,10 +86,11 @@ public class UnnecessaryUsingsTest
[DataRow("_ViewImports.razor")]
public void UnnecessaryUsings_RazorViewImportsSimilarRazorFile_IssuesReported(string fileName) =>
builder
.AddSnippet("@using System.Linq;", "_Imports.razor")
.AddSnippet(@"@using System.Text.Json; @* Noncompliant *@", fileName)
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, ProjectType.Product))
.Verify();
.AddSnippet("@using System.Linq;", "_Imports.razor")
.AddSnippet(@"@using System.Text.Json; @* Noncompliant *@", fileName)
.AddReferences(NuGetMetadataReference.SystemTextJson("7.0.4"))
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, ProjectType.Product))
.Verify();

[DataTestMethod]
[DataRow("_ViewImports.cs")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,176 +20,171 @@

using System.IO;
using SonarAnalyzer.AnalysisContext;
using SonarAnalyzer.Common;
using SonarAnalyzer.Protobuf;
using SonarAnalyzer.Rules;
using CS = SonarAnalyzer.Rules.CSharp;
using VB = SonarAnalyzer.Rules.VisualBasic;

namespace SonarAnalyzer.Test.Rules
namespace SonarAnalyzer.Test.Rules;

[TestClass]
public class CopyPasteTokenAnalyzerTest
{
[TestClass]
public class CopyPasteTokenAnalyzerTest
{
private const string BasePath = @"Utilities\CopyPasteTokenAnalyzer\";
private const string BasePath = @"Utilities\CopyPasteTokenAnalyzer\";

public TestContext TestContext { get; set; }
public TestContext TestContext { get; set; }

[TestMethod]
public void Verify_Unique_CS() =>
Verify("Unique.cs", info =>
{
info.Should().HaveCount(102);
info.Count(x => x.TokenValue == "$str").Should().Be(9);
info.Count(x => x.TokenValue == "$num").Should().Be(1);
info.Count(x => x.TokenValue == "$char").Should().Be(2);
});
[TestMethod]
public void Verify_Unique_CS() =>
Verify("Unique.cs", x =>
{
x.Should().HaveCount(102);
x.Count(token => token.TokenValue == "$str").Should().Be(9);
x.Count(token => token.TokenValue == "$num").Should().Be(1);
x.Count(token => token.TokenValue == "$char").Should().Be(2);
});

#if NET

[TestMethod]
public void Verify_Unique_CSharp11() =>
Verify("Unique.Csharp11.cs", info =>
{
info.Should().HaveCount(155);
info.Count(x => x.TokenValue == "$str").Should().Be(16);
info.Count(x => x.TokenValue == "$num").Should().Be(1);
info.Count(x => x.TokenValue == "$char").Should().Be(2);
});

[TestMethod]
public void Verify_Unique_CSharp12() =>
Verify("Unique.Csharp12.cs", info =>
{
info.Should().HaveCount(81);
info.Count(x => x.TokenValue == "$str").Should().Be(4);
info.Count(x => x.TokenValue == "$num").Should().Be(4);
info.Count(x => x.TokenValue == "$char").Should().Be(4);
});
[TestMethod]
public void Verify_Unique_CSharp11() =>
Verify("Unique.Csharp11.cs", x =>
{
x.Should().HaveCount(155);
x.Count(token => token.TokenValue == "$str").Should().Be(16);
x.Count(token => token.TokenValue == "$num").Should().Be(1);
x.Count(token => token.TokenValue == "$char").Should().Be(2);
});

[TestMethod]
public void Verify_Unique_CSharp12() =>
Verify("Unique.Csharp12.cs", x =>
{
x.Should().HaveCount(81);
x.Count(token => token.TokenValue == "$str").Should().Be(4);
x.Count(token => token.TokenValue == "$num").Should().Be(4);
x.Count(token => token.TokenValue == "$char").Should().Be(4);
});

#endif

[TestMethod]
public void Verify_Unique_VB() =>
Verify("Unique.vb", info =>
{
info.Should().HaveCount(88);
info.Where(x => x.TokenValue == "$str").Should().HaveCount(3);
info.Where(x => x.TokenValue == "$num").Should().HaveCount(7);
info.Should().ContainSingle(x => x.TokenValue == "$char");
});

[TestMethod]
public void Verify_Duplicated_CS() =>
Verify("Duplicated.cs", info =>
{
info.Should().HaveCount(39);
info.Where(x => x.TokenValue == "$num").Should().HaveCount(2);
});

[TestMethod]
public void Verify_Duplicated_CS_GlobalUsings() =>
CreateBuilder(ProjectType.Product, "Duplicated.CSharp10.cs")
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, ProjectType.Product))
.VerifyUtilityAnalyzer<CopyPasteTokenInfo>(messages =>
{
messages.Should().ContainSingle();
var info = messages.Single();
info.FilePath.Should().Be(Path.Combine(BasePath, "Duplicated.CSharp10.cs"));
info.TokenInfo.Should().HaveCount(39);
info.TokenInfo.Where(x => x.TokenValue == "$num").Should().HaveCount(2);
});

[TestMethod]
public void Verify_DuplicatedDifferentLiterals_CS() =>
Verify("DuplicatedDifferentLiterals.cs", info =>
[TestMethod]
public void Verify_Unique_VB() =>
Verify("Unique.vb", x =>
{
x.Should().HaveCount(88);
x.Where(token => token.TokenValue == "$str").Should().HaveCount(3);
x.Where(token => token.TokenValue == "$num").Should().HaveCount(7);
x.Should().ContainSingle(token => token.TokenValue == "$char");
});

[TestMethod]
public void Verify_Duplicated_CS() =>
Verify("Duplicated.cs", x =>
{
x.Should().HaveCount(39);
x.Where(token => token.TokenValue == "$num").Should().HaveCount(2);
});

[TestMethod]
public void Verify_Duplicated_CS_GlobalUsings() =>
CreateBuilder(ProjectType.Product, "Duplicated.CSharp10.cs")
.VerifyUtilityAnalyzer<CopyPasteTokenInfo>(x =>
{
info.Should().HaveCount(39);
info.Where(x => x.TokenValue == "$num").Should().HaveCount(2);
x.Should().ContainSingle();
var info = x.Single();
info.FilePath.Should().Be(Path.Combine(BasePath, "Duplicated.CSharp10.cs"));
info.TokenInfo.Should().HaveCount(39);
info.TokenInfo.Where(token => token.TokenValue == "$num").Should().HaveCount(2);
});

[TestMethod]
public void Verify_NotRunForTestProject_CS() =>
CreateBuilder(ProjectType.Test, "DuplicatedDifferentLiterals.cs").VerifyUtilityAnalyzerProducesEmptyProtobuf();

[DataTestMethod]
[DataRow("Unique.cs")]
[DataRow("SomethingElse.cs")]
public void Verify_UnchangedFiles(string unchangedFileName) =>
CreateBuilder(ProjectType.Product, "Unique.cs")
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfigWithUnchangedFiles(TestContext, BasePath + unchangedFileName))
.VerifyUtilityAnalyzer<TokenTypeInfo>(x => x.Should().NotBeEmpty());

private void Verify(string fileName, Action<IReadOnlyList<CopyPasteTokenInfo.Types.TokenInfo>> verifyTokenInfo) =>
CreateBuilder(ProjectType.Product, fileName)
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, ProjectType.Product))
.VerifyUtilityAnalyzer<CopyPasteTokenInfo>(messages =>
{
messages.Should().ContainSingle();
var info = messages.Single();
info.FilePath.Should().Be(Path.Combine(BasePath, fileName));
verifyTokenInfo(info.TokenInfo);
});
[TestMethod]
public void Verify_DuplicatedDifferentLiterals_CS() =>
Verify("DuplicatedDifferentLiterals.cs", x =>
{
x.Should().HaveCount(39);
x.Where(token => token.TokenValue == "$num").Should().HaveCount(2);
});

[TestMethod]
public void Verify_NotRunForTestProject_CS() =>
CreateBuilder(ProjectType.Test, "DuplicatedDifferentLiterals.cs").VerifyUtilityAnalyzerProducesEmptyProtobuf();

[DataTestMethod]
[DataRow("Unique.cs")]
[DataRow("SomethingElse.cs")]
public void Verify_UnchangedFiles(string unchangedFileName) =>
CreateBuilder(ProjectType.Product, "Unique.cs")
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfigWithUnchangedFiles(TestContext, BasePath + unchangedFileName))
.VerifyUtilityAnalyzer<TokenTypeInfo>(x => x.Should().NotBeEmpty());

private void Verify(string fileName, Action<IReadOnlyList<CopyPasteTokenInfo.Types.TokenInfo>> verifyTokenInfo) =>
CreateBuilder(ProjectType.Product, fileName)
.VerifyUtilityAnalyzer<CopyPasteTokenInfo>(x =>
{
x.Should().ContainSingle();
var info = x.Single();
info.FilePath.Should().Be(Path.Combine(BasePath, fileName));
verifyTokenInfo(info.TokenInfo);
});

#if NET

[DataTestMethod]
[DataRow("Razor.razor")]
[DataRow("Razor.cshtml")]
public void Verify_NoMetricsAreComputedForRazorFiles(string fileName) =>
CreateBuilder(ProjectType.Product, fileName)
.WithAdditionalFilePath(AnalysisScaffolding.CreateSonarProjectConfig(TestContext, ProjectType.Product))
.VerifyUtilityAnalyzer<CopyPasteTokenInfo>(messages => messages.Select(x => Path.GetFileName(x.FilePath)).Should().BeEmpty());
[DataTestMethod]
[DataRow("Razor.razor")]
[DataRow("Razor.cshtml")]
public void Verify_NoMetricsAreComputedForRazorFiles(string fileName) =>
CreateBuilder(ProjectType.Product, fileName)
.VerifyUtilityAnalyzer<CopyPasteTokenInfo>(x => x.Select(token => Path.GetFileName(token.FilePath)).Should().BeEmpty());

#endif

private VerifierBuilder CreateBuilder(ProjectType projectType, string fileName)
{
var testRoot = BasePath + TestContext.TestName;
var language = AnalyzerLanguage.FromPath(fileName);
UtilityAnalyzerBase analyzer = language.LanguageName switch
{
LanguageNames.CSharp => new TestCopyPasteTokenAnalyzer_CS(testRoot, projectType == ProjectType.Test),
LanguageNames.VisualBasic => new TestCopyPasteTokenAnalyzer_VB(testRoot, projectType == ProjectType.Test),
_ => throw new UnexpectedLanguageException(language)
};
return new VerifierBuilder()
.AddAnalyzer(() => analyzer)
.AddPaths(fileName)
.WithBasePath(BasePath)
.WithOptions(ParseOptionsHelper.Latest(language))
.WithProtobufPath(@$"{testRoot}\token-cpd.pb");
}

// We need to set protected properties and this class exists just to enable the analyzer without bothering with additional files with parameters
private sealed class TestCopyPasteTokenAnalyzer_CS : CS.CopyPasteTokenAnalyzer
private VerifierBuilder CreateBuilder(ProjectType projectType, string fileName)
{
var testRoot = BasePath + TestContext.TestName;
var language = AnalyzerLanguage.FromPath(fileName);
UtilityAnalyzerBase analyzer = language.LanguageName switch
{
private readonly string outPath;
private readonly bool isTestProject;
LanguageNames.CSharp => new TestCopyPasteTokenAnalyzer_CS(testRoot, projectType == ProjectType.Test),
LanguageNames.VisualBasic => new TestCopyPasteTokenAnalyzer_VB(testRoot, projectType == ProjectType.Test),
_ => throw new UnexpectedLanguageException(language)
};
return new VerifierBuilder()
.AddAnalyzer(() => analyzer)
.AddPaths(fileName)
.WithBasePath(BasePath)
.WithOptions(ParseOptionsHelper.Latest(language))
.WithProtobufPath(@$"{testRoot}\token-cpd.pb");
}

public TestCopyPasteTokenAnalyzer_CS(string outPath, bool isTestProject)
{
this.outPath = outPath;
this.isTestProject = isTestProject;
}
// We need to set protected properties and this class exists just to enable the analyzer without bothering with additional files with parameters
private sealed class TestCopyPasteTokenAnalyzer_CS : CS.CopyPasteTokenAnalyzer
{
private readonly string outPath;
private readonly bool isTestProject;

protected override UtilityAnalyzerParameters ReadParameters<T>(SonarAnalysisContextBase<T> context) =>
base.ReadParameters(context) with { IsAnalyzerEnabled = true, OutPath = outPath, IsTestProject = isTestProject };
public TestCopyPasteTokenAnalyzer_CS(string outPath, bool isTestProject)
{
this.outPath = outPath;
this.isTestProject = isTestProject;
}

private sealed class TestCopyPasteTokenAnalyzer_VB : VB.CopyPasteTokenAnalyzer
{
private readonly string outPath;
private readonly bool isTestProject;
protected override UtilityAnalyzerParameters ReadParameters<T>(SonarAnalysisContextBase<T> context) =>
base.ReadParameters(context) with { IsAnalyzerEnabled = true, OutPath = outPath, IsTestProject = isTestProject };
}

public TestCopyPasteTokenAnalyzer_VB(string outPath, bool isTestProject)
{
this.outPath = outPath;
this.isTestProject = isTestProject;
}
private sealed class TestCopyPasteTokenAnalyzer_VB : VB.CopyPasteTokenAnalyzer
{
private readonly string outPath;
private readonly bool isTestProject;

protected override UtilityAnalyzerParameters ReadParameters<T>(SonarAnalysisContextBase<T> context) =>
base.ReadParameters(context) with { IsAnalyzerEnabled = true, OutPath = outPath, IsTestProject = isTestProject };
public TestCopyPasteTokenAnalyzer_VB(string outPath, bool isTestProject)
{
this.outPath = outPath;
this.isTestProject = isTestProject;
}

protected override UtilityAnalyzerParameters ReadParameters<T>(SonarAnalysisContextBase<T> context) =>
base.ReadParameters(context) with { IsAnalyzerEnabled = true, OutPath = outPath, IsTestProject = isTestProject };
}
}

0 comments on commit c755a17

Please sign in to comment.