Skip to content

Commit

Permalink
chore: implement integration testing (#124)
Browse files Browse the repository at this point in the history
* some setup

* make it build and execute without AOT

* at this moment - i dont know

* minimal assembly references

* bump packages and then it does not work still...

* do it iteratively

* move pieces around

* make it work with executable assembly!

* omg it works.

* use existing local machine table name

* record stack traces

* remove package versions

* refactor dependencies

* YES!!! YES!!! YES!!! YES!!! YES!!! YES!!! YES!!!

* fix dbstring global namespace + adjust test for DbString

* and run tests for new proj

* last minute review

* rollback package updates (they break tests)

* remove coverlet collector

* use sln

* internal visible to

* try specific routes

* dont build it

* try with ubuntu agents

* rollback CI\CD + create another for integrations on ubuntu

* fix for dbstring + rename CI

* debug test

* fix naming

* try publish directory

* move item group

* get logs from build

* maybe slash can fix it?

* remove detailed verbosity

* .

* always?

* test on wsl

* build only test proj

* move it to integration directly

* give me logs

* dont use copy at all

* cleanup debug CI

* move to csproj
  • Loading branch information
DeagleGross authored Aug 23, 2024
1 parent 21b383a commit a9328b1
Show file tree
Hide file tree
Showing 22 changed files with 542 additions and 44 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/dotnet-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Integration Tests

on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:

runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # depth is needed for nbgv

- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: |
6.0.x
8.0.x
include-prerelease: true

- name: Build
run: dotnet build Dapper.AOT.sln -c Debug

- name: E2E Tests
run: dotnet test test/Dapper.AOT.Test.Integration/Dapper.AOT.Test.Integration.csproj --no-build --verbosity normal -c Debug
74 changes: 37 additions & 37 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,40 @@ jobs:
runs-on: windows-latest

steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # depth is needed for nbgv
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: |
6.0.x
8.0.x
include-prerelease: true
- uses: dotnet/nbgv@master
with:
setAllVars: true

- name: Restore dependencies
run: dotnet restore Build.csproj

- name: Purge
run: del src/Dapper.*/bin/Release/Dapper.*.nupkg
- name: Build
run: dotnet build Build.csproj --no-restore -c Release

- name: Test
run: dotnet test Build.csproj --no-build --verbosity normal -c Release -f net6.0 --filter FullyQualifiedName!~Integration

- name: Pack
if: ${{ success() && !github.base_ref }}
run: dotnet pack src/Dapper.AOT/Dapper.AOT.csproj --no-build --verbosity normal -c Release

- name: Push to MyGet
if: ${{ success() && !github.base_ref }}
run: dotnet nuget push src/Dapper.*/bin/Release/Dapper.*.nupkg --source https://www.myget.org/F/dapper/api/v2/package --api-key "$env:MYGETAPIKEY"
env:
MYGETAPIKEY: ${{ secrets.MYGETAPIKEY }}
- uses: actions/checkout@v2
with:
fetch-depth: 0 # depth is needed for nbgv

- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: |
6.0.x
8.0.x
include-prerelease: true

- uses: dotnet/nbgv@master
with:
setAllVars: true

- name: Restore dependencies
run: dotnet restore Build.csproj

- name: Purge
run: del src/Dapper.*/bin/Release/Dapper.*.nupkg

- name: Build
run: dotnet build Build.csproj --no-restore -c Release

- name: Test
run: dotnet test Build.csproj --no-build --verbosity normal -c Release -f net6.0 --filter FullyQualifiedName!~Integration

- name: Pack
if: ${{ success() && !github.base_ref }}
run: dotnet pack src/Dapper.AOT/Dapper.AOT.csproj --no-build --verbosity normal -c Release

- name: Push to MyGet
if: ${{ success() && !github.base_ref }}
run: dotnet nuget push src/Dapper.*/bin/Release/Dapper.*.nupkg --source https://www.myget.org/F/dapper/api/v2/package --api-key "$env:MYGETAPIKEY"
env:
MYGETAPIKEY: ${{ secrets.MYGETAPIKEY }}
15 changes: 15 additions & 0 deletions Dapper.AOT.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
.github\workflows\dotnet-integration.yml = .github\workflows\dotnet-integration.yml
global.json = global.json
nuget.config = nuget.config
README.md = README.md
Expand All @@ -40,6 +41,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docs", "docs\docs.csproj",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UsageVanilla", "test\UsageVanilla\UsageVanilla.csproj", "{840EA1CA-62FF-409E-89F5-CD3BB269BAE3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.AOT.Test.Integration.Executables", "test\Dapper.AOT.Test.Integration.Executables\Dapper.AOT.Test.Integration.Executables.csproj", "{9FA6A3B4-5722-40B2-8B58-F418F5D8C9A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.AOT.Test.Integration", "test\Dapper.AOT.Test.Integration\Dapper.AOT.Test.Integration.csproj", "{CAB51448-4F0D-497B-A035-2B7ACB2DD9EB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -78,6 +83,14 @@ Global
{840EA1CA-62FF-409E-89F5-CD3BB269BAE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{840EA1CA-62FF-409E-89F5-CD3BB269BAE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{840EA1CA-62FF-409E-89F5-CD3BB269BAE3}.Release|Any CPU.Build.0 = Release|Any CPU
{9FA6A3B4-5722-40B2-8B58-F418F5D8C9A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9FA6A3B4-5722-40B2-8B58-F418F5D8C9A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9FA6A3B4-5722-40B2-8B58-F418F5D8C9A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9FA6A3B4-5722-40B2-8B58-F418F5D8C9A8}.Release|Any CPU.Build.0 = Release|Any CPU
{CAB51448-4F0D-497B-A035-2B7ACB2DD9EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CAB51448-4F0D-497B-A035-2B7ACB2DD9EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAB51448-4F0D-497B-A035-2B7ACB2DD9EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAB51448-4F0D-497B-A035-2B7ACB2DD9EB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -91,6 +104,8 @@ Global
{A77B633C-573E-43CD-85A4-8063B33143B4} = {FE215D4B-811B-47BB-9F05-6382DD1C6729}
{C6527566-38F4-43CC-9E0E-91C4B8854774} = {1135D4FD-770E-41DF-920B-A8F75E42A832}
{840EA1CA-62FF-409E-89F5-CD3BB269BAE3} = {9A846B95-90CE-4335-9043-48C5B8EA4FB8}
{9FA6A3B4-5722-40B2-8B58-F418F5D8C9A8} = {9A846B95-90CE-4335-9043-48C5B8EA4FB8}
{CAB51448-4F0D-497B-A035-2B7ACB2DD9EB} = {9A846B95-90CE-4335-9043-48C5B8EA4FB8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A89CDAFA-494F-4168-9648-1138BA738D43}
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.7.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
<!-- 4.8 would be nice, but: let's try to offer down-level compiler support -->
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="[4.4.0]" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing.XUnit" Version="1.1.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading;
using static Dapper.Internal.Inspection;
Expand All @@ -26,12 +25,33 @@ namespace Dapper.CodeAnalysis;
[Generator(LanguageNames.CSharp), DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed partial class DapperInterceptorGenerator : InterceptorGeneratorBase
{
private readonly bool _withInterceptionRecording = false;

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => DiagnosticsBase.All<Diagnostics>();

#pragma warning disable CS0067 // unused; retaining for now
public event Action<string>? Log;
#pragma warning restore CS0067

/// <summary>
/// Creates an interceptor generator for Dapper
/// </summary>
public DapperInterceptorGenerator()
{
}

/// <summary>
/// Creates an interceptor generator for Dapper used for Tests.
/// </summary>
/// <note>
/// It will insert very specific call with known method name.
/// Users will not have a reference to inserted assembly code, therefore: don't make it public
/// </note>
internal DapperInterceptorGenerator(bool withInterceptionRecording)
{
_withInterceptionRecording = withInterceptionRecording;
}

public override void Initialize(IncrementalGeneratorInitializationContext context)
{
var nodes = context.SyntaxProvider.CreateSyntaxProvider(PreFilter, Parse)
Expand Down Expand Up @@ -360,6 +380,13 @@ internal void Generate(in GenerateState ctx)

sb.NewLine();

if (_withInterceptionRecording)
{
sb.Append("// record interception for tests assertions").NewLine();
sb.Append("global::Dapper.AOT.Test.Integration.Executables.Recording.InterceptorRecorderResolver.Resolve().Record();").NewLine();
sb.NewLine();
}

if (flags.HasAny(OperationFlags.GetRowParser))
{
WriteGetRowParser(sb, resultType, readers);
Expand Down
4 changes: 4 additions & 0 deletions src/Dapper.AOT.Analyzers/Dapper.AOT.Analyzers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,8 @@
<DependentUpon>DapperAnalyzer.cs</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Dapper.AOT.Test.Integration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a17ba361da0990b3da23f3c20f2a002242397b452a28f27832d61d49f35edb54a68b98d98557b8a02be79be42142339c7861af309c8917dee972775e2c358dd6b96109a9147987652b25b8dc52e7f61f22a755831674f0a3cea17bef9abb6b23ef1856a02216864a1ffbb04a4c549258d32ba740fe141dad2f298a8130ea56d0" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion src/Dapper.AOT.Analyzers/Internal/Inspection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ public DapperSpecialType DapperSpecialType
ContainingNamespace:
{
Name: "Dapper",
IsGlobalNamespace: true
ContainingNamespace.IsGlobalNamespace: true
}
}) return DapperSpecialType.DbString;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net6.0;net48</TargetFrameworks>
<RootNamespace>Dapper.AOT.Test.Integration.Executables</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dapper" Condition="'$(TargetFramework)' == 'net6.0'" />
<PackageReference Include="Dapper.StrongName" Condition="'$(TargetFramework)'=='net48'" />
<PackageReference Include="Dapper" Condition="'$(TargetFramework)' == 'net8.0'" />

<ProjectReference Include="..\..\src\Dapper.AOT.Analyzers\Dapper.AOT.Analyzers.csproj" />
<ProjectReference Include="..\..\src\Dapper.AOT\Dapper.AOT.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Dapper.AOT.Test.Integration.Executables.Recording;
using Dapper.CodeAnalysis;

namespace Dapper.AOT.Test.Integration.Executables;

public class ExceptedCodeInterceptorRecorder<TExecutable> : IInterceptorRecorder
{
readonly string expectedFileName;

public ExceptedCodeInterceptorRecorder(string expectedFileName)
{
this.expectedFileName = expectedFileName ?? throw new ArgumentNullException(nameof(expectedFileName));
}

public bool WasCalled { get; private set; }
public string? Diagnostics { get; private set; }

public void Record()
{
WasCalled = true;
var diagnostics = new StringBuilder();

var stackTrace = new StackTrace(fNeedFileInfo: true);
var recentFrames = stackTrace.GetFrames().Take(15).ToList(); // we dont need everything

var userCodeFrameIndex = recentFrames.FindIndex(frame =>
frame.GetFileName()?.Contains(expectedFileName) == true && frame.GetMethod()?.Name.Equals(nameof(IExecutable<TExecutable>.Execute)) == true);
if (userCodeFrameIndex == -1)
{
diagnostics.AppendLine("- User code execution is not found");
}

var dapperInterceptionFrameIndex = recentFrames.FindIndex(frame =>
frame.GetFileName()?.Contains(".generated.cs") == true && frame.GetFileName()?.Contains(nameof(DapperInterceptorGenerator)) == true);
if (dapperInterceptionFrameIndex == -1)
{
diagnostics.AppendLine("- User code execution is not found");
}

if (userCodeFrameIndex < dapperInterceptionFrameIndex)
{
diagnostics.AppendLine("- User code call should be higher (executed before) on the stack trace than intercepted code");
}

Diagnostics = diagnostics.ToString();
}
}
8 changes: 8 additions & 0 deletions test/Dapper.AOT.Test.Integration.Executables/IExecutable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Data;

namespace Dapper.AOT.Test.Integration.Executables;

public interface IExecutable<T>
{
public T Execute(IDbConnection connection);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Dapper.AOT.Test.Integration.Executables.Models;

public class DbStringPoco
{
public const string TableName = "dbStringPoco";

public int Id { get; set; }
public string Name { get; set; }

Check warning on line 8 in test/Dapper.AOT.Test.Integration.Executables/Models/DbStringPoco.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 8 in test/Dapper.AOT.Test.Integration.Executables/Models/DbStringPoco.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 8 in test/Dapper.AOT.Test.Integration.Executables/Models/DbStringPoco.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 8 in test/Dapper.AOT.Test.Integration.Executables/Models/DbStringPoco.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 8 in test/Dapper.AOT.Test.Integration.Executables/Models/DbStringPoco.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 8 in test/Dapper.AOT.Test.Integration.Executables/Models/DbStringPoco.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Dapper.AOT.Test.Integration.Executables.Recording;

public interface IInterceptorRecorder
{
/// <summary>
/// returns true if expected interception was called
/// </summary>
public bool WasCalled { get; }

/// <summary>
/// Returns diagnostics of recording
/// </summary>
public string? Diagnostics { get; }

/// <summary>
/// Is executed in the interception
/// </summary>
public void Record();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Dapper.AOT.Test.Integration.Executables.Recording;

public static class InterceptorRecorderResolver
{
static IInterceptorRecorder? _recorder;

public static IInterceptorRecorder? Resolve() => _recorder;
public static void Register(IInterceptorRecorder? recorder) => _recorder = recorder;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Data;
using System.Linq;
using Dapper.AOT.Test.Integration.Executables.Models;

namespace Dapper.AOT.Test.Integration.Executables.UserCode;

[DapperAot]
public class DbStringUsage : IExecutable<DbStringPoco>
{
public DbStringPoco Execute(IDbConnection connection)
{
var results = connection.Query<DbStringPoco>(
$"select * from {DbStringPoco.TableName} where name = @name",
new
{
name = new DbString
{
Value = "my-dbString"
}
});
return results.First();
}
}
Loading

0 comments on commit a9328b1

Please sign in to comment.