Skip to content

Commit

Permalink
working on defining assembly contexts (#3)
Browse files Browse the repository at this point in the history
* working on defining assembly contexts

Basic Roslyn context to define rules and whether or not they apply to the current unit of compilation.

* working on `IAkkaContext`

* cleanup

* finished base class definitions

* cleaning up

* starting work on semantic models for first rule

* push

* have analyzer syntax working

* adding test project

* defined first spec for testing

* working on fixing tests

* first happy path spec is passing

* fdgdfg

* have test setup working correctly

* added test cases to check for other ActorBase implementations

* fixed `IsActorBaseSubclass`
  • Loading branch information
Aaronontheweb authored Dec 28, 2023
1 parent cf2ba49 commit d72b1eb
Show file tree
Hide file tree
Showing 21 changed files with 710 additions and 12 deletions.
20 changes: 16 additions & 4 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<Project>
<PropertyGroup>
<Copyright>Copyright © 2023 Your Company</Copyright>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn);CS1591;NU1701;CA1707;</NoWarn>
<VersionPrefix>1.0.0</VersionPrefix>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

<PropertyGroup Label="Build">
Expand All @@ -11,7 +12,18 @@
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup Label="Dependencies">
<PbmVersion>1.3.0</PbmVersion>
</PropertyGroup>
<!-- ======================================== -->
<!-- Production-specific properties and items -->

<Choose>
<When Condition=" !$(MSBuildProjectName.Contains('.tests')) ">
<PropertyGroup>
<AnalysisLevel>latest-All</AnalysisLevel>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)signing.snk</AssemblyOriginatorKeyFile>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>
</When>
</Choose>
</Project>
15 changes: 13 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>

<!-- Akka.NET Package Versions -->
<ItemGroup>
<PackageVersion Include="Akka.Hosting" Version="1.5.6.1" />
<PackageVersion Include="Microsoft.CodeAnalysis" Version="4.8.0" />
</ItemGroup>
</Project>

<!-- Testing Utilities -->
<ItemGroup>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageVersion Include="xunit" Version="2.5.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.0" />
<PackageVersion Include="FluentAssertions" Version="6.11.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
</Project>
14 changes: 14 additions & 0 deletions akka.analyzers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Analyzers", "src\Akka.Analyzers\Akka.Analyzers.csproj", "{C9D5BDA8-E4AF-4A83-948B-C1010A5592CE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{B67B6FBE-1946-404D-BE35-462B6EEAF1E2}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
global.json = global.json
nuget.config = nuget.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Analyzers.Tests", "src\Akka.Analyzers.Tests\Akka.Analyzers.Tests.csproj", "{21D3D1DE-CC6D-435C-B025-FB232D3D1A6D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -18,5 +28,9 @@ Global
{C9D5BDA8-E4AF-4A83-948B-C1010A5592CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9D5BDA8-E4AF-4A83-948B-C1010A5592CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9D5BDA8-E4AF-4A83-948B-C1010A5592CE}.Release|Any CPU.Build.0 = Release|Any CPU
{21D3D1DE-CC6D-435C-B025-FB232D3D1A6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21D3D1DE-CC6D-435C-B025-FB232D3D1A6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21D3D1DE-CC6D-435C-B025-FB232D3D1A6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21D3D1DE-CC6D-435C-B025-FB232D3D1A6D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
41 changes: 41 additions & 0 deletions src/Akka.Analyzers.Tests/Akka.Analyzers.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<!-- Download packages referenced by ReferenceAssembliesHelper -->
<PackageDownload Include="Akka" Version="[1.5.14]" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="xunit"/>
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="FluentAssertions"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Akka.Analyzers\Akka.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Akka.Analyzers\Akka.Analyzers.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// -----------------------------------------------------------------------
// <copyright file="MustNotUseNewKeywordOnActorSpecs.cs" company="Akka.NET Project">
// Copyright (C) 2015-2023 .NET Petabridge, LLC
// </copyright>
// -----------------------------------------------------------------------

using Akka.Analyzers.Tests.Utility;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using static Akka.Analyzers.RuleDescriptors;
using Verify = Akka.Analyzers.Tests.Utility.AkkaVerifier<Akka.Analyzers.MustNotUseNewKeywordOnActorsAnalyzer>;

namespace Akka.Analyzers.Tests.Analyzers.AK1000;

public class AkkaActorInstantiationAnalyzerTests
{
public static readonly TheoryData<string> SuccessCases = new()
{
// ActorBase
"""
using Akka.Actor;
class MyActor : ActorBase {
protected override bool Receive(object message) {
return true;
}
}
class Test
{
void Method()
{
ActorSystem sys = ActorSystem.Create("MySys");
Props props = Props.Create(() => new MyActor());
IActorRef realActorInstance = sys.ActorOf(props);
}
}
""",

// UntypedActor
"""
using Akka.Actor;
class MyActor : UntypedActor {
protected override void OnReceive(object message) {
}
}
class Test
{
void Method()
{
ActorSystem sys = ActorSystem.Create("MySys");
Props props = Props.Create(() => new MyActor());
IActorRef realActorInstance = sys.ActorOf(props);
}
}
""",

// ReceiveActor
"""
using Akka.Actor;
class MyActor : ReceiveActor {
public MyActor(){
ReceiveAny(_ => { });
}
}
class Test
{
void Method()
{
ActorSystem sys = ActorSystem.Create("MySys");
Props props = Props.Create(() => new MyActor());
IActorRef realActorInstance = sys.ActorOf(props);
}
}
"""
};

[Theory]
[MemberData(nameof(SuccessCases))]
public async Task SuccessCase(string testCode)
{
await Verify.VerifyAnalyzer(testCode).ConfigureAwait(true);
}

[Fact]
public async Task FailureCase()
{
const string testCode = """
using Akka.Actor;
class MyActor : ActorBase {
protected override bool Receive(object message) {
return true;
}
}
class Test
{
void Method()
{
MyActor actorInstance = new MyActor();
}
}
""";

var expected = Verify.Diagnostic()
.WithSpan(13, 33, 13, 46)
.WithArguments("MyActor")
.WithSeverity(DiagnosticSeverity.Error);

await Verify.VerifyAnalyzer(testCode, expected).ConfigureAwait(true);
}
}
1 change: 1 addition & 0 deletions src/Akka.Analyzers.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
61 changes: 61 additions & 0 deletions src/Akka.Analyzers.Tests/Utility/AkkaVerifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// -----------------------------------------------------------------------
// <copyright file="AkkaVerifier.cs" company="Akka.NET Project">
// Copyright (C) 2015-2023 .NET Petabridge, LLC
// </copyright>
// -----------------------------------------------------------------------

using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;

namespace Akka.Analyzers.Tests.Utility;

[SuppressMessage("Design", "CA1000:Do not declare static members on generic types")]
public sealed class AkkaVerifier<TAnalyzer> where TAnalyzer : DiagnosticAnalyzer, new()
{
/// <summary>
/// Creates a diagnostic result for the diagnostic referenced in <see cref="TAnalyzer"/>.
/// </summary>
public static DiagnosticResult Diagnostic() =>
CSharpCodeFixVerifier<TAnalyzer, EmptyCodeFixProvider, DefaultVerifier>.Diagnostic();

public static Task VerifyAnalyzer(string source, params DiagnosticResult[] diagnostics)
{
return VerifyAnalyzer(new[] { source }, diagnostics);
}

public static Task VerifyAnalyzer(string[] sources, params DiagnosticResult[] diagnostics)
{
Guard.AssertIsNotNull(sources);

var test = new AkkaTest();
#pragma warning disable CA1062
foreach (var source in sources)
#pragma warning restore CA1062
test.TestState.Sources.Add(source);

test.ExpectedDiagnostics.AddRange(diagnostics);
return test.RunAsync();
}

private sealed class AkkaTest() : TestBase(ReferenceAssembliesHelper.CurrentAkka);

private class TestBase : CSharpAnalyzerTest<TAnalyzer, DefaultVerifier>
{
protected TestBase(ReferenceAssemblies referenceAssemblies)
{
ReferenceAssemblies = referenceAssemblies;

// Diagnostics are reported in both normal and generated code
TestBehaviors |= TestBehaviors.SkipGeneratedCodeCheck;

// Tests that check for messages should run independent of current system culture.
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
}
}
}
39 changes: 39 additions & 0 deletions src/Akka.Analyzers.Tests/Utility/ReferenceAssembliesHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// -----------------------------------------------------------------------
// <copyright file="ReferenceAssembliesHelper.cs" company="Akka.NET Project">
// Copyright (C) 2015-2023 .NET Petabridge, LLC
// </copyright>
// -----------------------------------------------------------------------

using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Testing;

namespace Akka.Analyzers.Tests.Utility;

/// <summary>
/// Used to help us load the correct reference assemblies for the versions of Akka.NET being tested.
/// </summary>
/// <remarks>
/// When making changes to any of these assemblies, make sure to update the 'PackageDownload' elements
/// inside of the 'Akka.Analyzers.Tests.csproj' file.
/// </remarks>
internal static class ReferenceAssembliesHelper
{
public static readonly ReferenceAssemblies CurrentAkka;

#pragma warning disable CA1810
static ReferenceAssembliesHelper()
#pragma warning restore CA1810
{
// Can't use ReferenceAssemblies.Net.Net80 because it's too new for Microsoft.CodeAnalysis 4.2.0
var defaultAssemblies =
new ReferenceAssemblies(
"net8.0",
new PackageIdentity("Microsoft.NETCore.App.Ref", "8.0.0"),
Path.Combine("ref", "net8.0")
);

CurrentAkka = defaultAssemblies.AddPackages(
[new PackageIdentity("Akka", "1.5.14")]
);
}
}
Loading

0 comments on commit d72b1eb

Please sign in to comment.