Skip to content

Commit

Permalink
Merge pull request #140 from dragonfruitnetwork/2024-sourcegen-rewrite
Browse files Browse the repository at this point in the history
2024 Source Generator Rewrite
  • Loading branch information
aspriddell authored Dec 16, 2023
2 parents 8f28c97 + 4ed82c6 commit d583601
Show file tree
Hide file tree
Showing 130 changed files with 3,384 additions and 2,803 deletions.
4 changes: 2 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
"isRoot": true,
"tools": {
"jetbrains.resharper.globaltools": {
"version": "2021.2.2",
"version": "2023.3.1",
"commands": [
"jb"
]
},
"nvika": {
"version": "2.2.0",
"version": "3.0.0",
"commands": [
"nvika"
]
Expand Down
44 changes: 19 additions & 25 deletions .github/workflows/codequality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,30 @@ on:
push:
branches: master

env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: false

jobs:
quality:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Install .NET 3.1.x LTS
uses: actions/setup-dotnet@v1
with:
dotnet-version: "3.1.x"

- name: Install .NET 5.0.x
uses: actions/setup-dotnet@v1
with:
dotnet-version: "5.0.x"

- name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1
with:
dotnet-version: "6.0.x"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
8.0.x
- name: Restore Tools
run: dotnet tool restore
- name: Restore Tools
run: dotnet tool restore

- name: Restore Packages
run: dotnet restore
- name: Restore Packages
run: dotnet restore

- name: InspectCode
run: dotnet jb inspectcode ${{github.workspace}}/DragonFruit.Data.sln --output=${{github.workspace}}/inspectcodereport.xml --cachesDir=${{github.workspace}}/inspectcode --verbosity=WARN --no-build
- name: InspectCode
run: dotnet jb inspectcode DragonFruit.Data.sln --output=inspectcodereport.xml --cachesDir=inspectcode --verbosity=WARN --no-build

- name: NVika
run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml"
- name: NVika
run: dotnet nvika parsereport inspectcodereport.xml
27 changes: 11 additions & 16 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,28 @@ on:
types: [ published ]

jobs:
deploy:
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v3
publish:
runs-on: ubuntu-latest

- name: Install .NET
uses: actions/setup-dotnet@v2
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.0.x'
dotnet-version: "6.0.x"

- name: Restore
run: |
dotnet workload install android
dotnet restore
run: dotnet restore

- name: Build
run: dotnet build -c Release -v normal -p:EnableAndroid=true -p:Version=${{ github.ref_name }}
run: dotnet build -c Release -v normal -p:Version=${{ github.event.release.tag_name }}

- name: Pack (Beta)
run: dotnet pack -c Release --include-symbols --no-build -v normal -o . -p:EnableAndroid=true -p:PackageVersion=${{ github.ref_name }}-beta
run: dotnet pack -c Release --include-symbols --no-build -v normal -o . -p:PackageVersion=${{ github.event.release.tag_name }}-beta
if: "github.event.release.prerelease"

- name: Pack (Stable)
run: dotnet pack -c Release --include-symbols --no-build -v normal -o . -p:EnableAndroid=true -p:PackageVersion=${{ github.ref_name }}
run: dotnet pack -c Release --include-symbols --no-build -v normal -o . -p:PackageVersion=${{ github.event.release.tag_name }}
if: "!github.event.release.prerelease"

- name: Publish
run: dotnet nuget push "*.nupkg" -k ${{ secrets.NUGET_KEY }} --skip-duplicate -s https://api.nuget.org/v3/index.json
run: dotnet nuget push "*.nupkg" -k ${{ secrets.NUGET_KEY }} --skip-duplicate -s https://api.nuget.org/v3/index.json
25 changes: 8 additions & 17 deletions .github/workflows/unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,21 @@ env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: false

testproject: './DragonFruit.Common.Data.Tests/DragonFruit.Common.Data.Tests.csproj'

jobs:
test:
runs-on: ubuntu-latest

strategy:
matrix:
include:
- dotnet: '6.0.x'
dotnet-tfm: 'net6.0'
dotnet-test-tfm: 'net6.0'

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Install .NET
uses: actions/setup-dotnet@v1
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet }}
dotnet-version: 8.0.x

- name: Build
run: dotnet build -c Debug
run: dotnet build -c Release

- name: Unit Tests (Roslyn)
run: dotnet test -f net8.0 -c Release --no-build --no-restore --verbosity normal DragonFruit.Data.Roslyn.Tests

- name: Unit Tests
run: dotnet test -f ${{ matrix.dotnet-test-tfm }}
run: dotnet test -f net8.0 -c Release --no-build --no-restore --verbosity normal DragonFruit.Data.Tests
122 changes: 122 additions & 0 deletions DragonFruit.Data.Roslyn.Tests/ApiRequestAnalyzerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DragonFruit.Data.Roslyn.Fixes;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Xunit;

namespace DragonFruit.Data.Roslyn.Tests
{
public class ApiRequestAnalyzerTests
{
private readonly string _testDataPath;

public ApiRequestAnalyzerTests()
{
_testDataPath = Path.Combine(GetSolutionRoot(), "DragonFruit.Data.Roslyn.Tests", "_TestData");
}

[Fact]
public async Task TestNonPartialClassDetectionAndFix()
{
var test = new CSharpCodeFixTest<ApiRequestAnalyzer, ApiRequestClassFixProvider, DefaultVerifier>
{
TestCode = await File.ReadAllTextAsync(Path.Combine(_testDataPath, "DA0001.cs")),
FixedCode = await File.ReadAllTextAsync(Path.Combine(_testDataPath, "DA0001.Fix.cs")),
ExpectedDiagnostics = { DiagnosticResult.CompilerError(ApiRequestAnalyzer.PartialClassRule.Id).WithSpan(8, 18, 8, 24).WithArguments("DA0001") }
};

await PerformTest(test);
}

public static readonly TheoryData<string, DiagnosticResult[]> AnalyzerDetectionData = new()
{
{
// DA0002: nested class
"DA0002.cs", new[] { DiagnosticResult.CompilerError(ApiRequestAnalyzer.NestedClassNotAllowedRule.Id).WithSpan(10, 30, 10, 40).WithArguments("DA0002_Req") }
},
{
// DA0003: property has no getter
"DA0003.cs", new[] { DiagnosticResult.CompilerWarning(ApiRequestAnalyzer.PropertyNoGetterRule.Id).WithSpan(14, 20, 14, 26).WithArguments("Param2") }
},
{
// DA0004: property or method not in apirequest
"DA0004.cs", new[]
{
// method not in apirequest
DiagnosticResult.CompilerWarning(ApiRequestAnalyzer.PropertyOrMethodNotInApiRequestRule.Id).WithSpan(11, 23, 11, 32).WithArguments("NotAParam"),

// property not in apirequest
DiagnosticResult.CompilerWarning(ApiRequestAnalyzer.PropertyOrMethodNotInApiRequestRule.Id).WithSpan(14, 20, 14, 28).WithArguments("TestData"),
}
},
{
// DA0005: property or method is inaccessible
"DA0005.cs", new[]
{
// private getter with public setter
DiagnosticResult.CompilerWarning(ApiRequestAnalyzer.PropertyOrMethodInaccessibleRule.Id).WithSpan(11, 19, 11, 28).WithArguments("Parameter"),

// private method
DiagnosticResult.CompilerWarning(ApiRequestAnalyzer.PropertyOrMethodInaccessibleRule.Id).WithSpan(14, 18, 14, 32).WithArguments("IsParameterSet"),
}
},
{
// DA0006: method returns void
"DA0006.cs", new[]
{
DiagnosticResult.CompilerError(ApiRequestAnalyzer.MethodReturnsVoidRule.Id).WithSpan(11, 17, 11, 28).WithArguments("GetUserData")
}
},
{
// DA0007: method has parameters
"DA0007.cs", new[]
{
DiagnosticResult.CompilerError(ApiRequestAnalyzer.MethodHasParametersRule.Id).WithSpan(11, 19, 11, 25).WithArguments("UserId")
}
}
};

[Theory]
[MemberData(nameof(AnalyzerDetectionData))]
public async Task TestRequestAnalysisDetections(string fileName, DiagnosticResult[] diagnosticResults)
{
var test = new CSharpAnalyzerTest<ApiRequestAnalyzer, DefaultVerifier>
{
TestCode = await File.ReadAllTextAsync(Path.Combine(_testDataPath, fileName))
};

test.ExpectedDiagnostics.AddRange(diagnosticResults);

await PerformTest(test);
}

private async Task PerformTest(AnalyzerTest<DefaultVerifier> test)
{
var content = ("Common.cs", await File.ReadAllTextAsync(Path.Combine(_testDataPath, "Common.cs")));

// add common.cs to test sources
test.TestState.Sources.Add(content);

if (test is CodeFixTest<DefaultVerifier> verifier)
{
verifier.FixedState.Sources.Add(content);
}

await test.RunAsync();
}

private string GetSolutionRoot()
{
var currentDirectory = Directory.GetCurrentDirectory();

while (Directory.EnumerateFiles(currentDirectory).All(x => Path.GetFileName(x) != "DragonFruit.Data.sln"))
{
currentDirectory = Path.Combine(currentDirectory, "..");
}

return Path.GetFullPath(currentDirectory);
}
}
}
31 changes: 31 additions & 0 deletions DragonFruit.Data.Roslyn.Tests/ApiRequestTemplateTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// DragonFruit.Data Copyright DragonFruit Network
// Licensed under the MIT License. Please refer to the LICENSE file at the root of this project for details

using System.IO;
using System.Threading.Tasks;
using Scriban;
using Xunit;

namespace DragonFruit.Data.Roslyn.Tests
{
public class ApiRequestTemplateTests
{
[Fact]
public async Task TestTemplateParse()
{
var assembly = typeof(ApiRequestSourceGenerator).Assembly;
using var template = assembly.GetManifestResourceStream(ApiRequestSourceGenerator.TemplateName);

Assert.NotNull(template);

using var templateReader = new StreamReader(template);
var templateText = await templateReader.ReadToEndAsync();

Assert.True(templateText.Length > 0);

var templateAst = Template.ParseLiquid(templateText);

Assert.False(templateAst.HasErrors);
}
}
}
30 changes: 30 additions & 0 deletions DragonFruit.Data.Roslyn.Tests/DragonFruit.Data.Roslyn.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.1"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Scriban" Version="5.9.0" />
<PackageReference Include="xunit" Version="2.6.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<None Remove="_TestData\**" />
<Compile Remove="_TestData\**"/>
<EmbeddedResource Remove="_TestData\**" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DragonFruit.Data.Roslyn\DragonFruit.Data.Roslyn.csproj" OutputItemType="Analyzer"/>
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions DragonFruit.Data.Roslyn.Tests/_TestData/Common.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace DragonFruit.Data
{
public class ApiRequest;
}

namespace DragonFruit.Data.Requests
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public class RequestParameterAttribute : Attribute;

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public class RequestBodyAttribute : Attribute;
}
13 changes: 13 additions & 0 deletions DragonFruit.Data.Roslyn.Tests/_TestData/DA0001.Fix.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using DragonFruit.Data.Requests;

namespace DragonFruit.Data.Roslyn.Tests.TestData
{
/// <summary>
/// Dummy request with no partial class modifier (DA0001)
/// </summary>
public partial class DA0001 : ApiRequest
{
[RequestParameter]
public string TestParam { get; set; }
}
}
13 changes: 13 additions & 0 deletions DragonFruit.Data.Roslyn.Tests/_TestData/DA0001.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using DragonFruit.Data.Requests;

namespace DragonFruit.Data.Roslyn.Tests.TestData
{
/// <summary>
/// Dummy request with no partial class modifier (DA0001)
/// </summary>
public class DA0001 : ApiRequest
{
[RequestParameter]
public string TestParam { get; set; }
}
}
Loading

0 comments on commit d583601

Please sign in to comment.