Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smoketests #28

Merged
merged 3 commits into from
Apr 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions Mediator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleStreaming", "samples\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASPNET", "samples\ASPNET\ASPNET.csproj", "{35D4B136-E164-48A3-ADC3-E22237CCDC9C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediator.SourceGenerator.Roslyn38", "src\Mediator.SourceGenerator.Roslyn38\Mediator.SourceGenerator.Roslyn38.csproj", "{FCB31060-4C23-4BB7-9A95-4C636B43F6B5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mediator.SourceGenerator.Roslyn38", "src\Mediator.SourceGenerator.Roslyn38\Mediator.SourceGenerator.Roslyn38.csproj", "{FCB31060-4C23-4BB7-9A95-4C636B43F6B5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediator.SourceGenerator.Roslyn40", "src\Mediator.SourceGenerator.Roslyn40\Mediator.SourceGenerator.Roslyn40.csproj", "{C5AD8C4D-D731-4DDD-96BD-A6776A1FF20E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mediator.SourceGenerator.Roslyn40", "src\Mediator.SourceGenerator.Roslyn40\Mediator.SourceGenerator.Roslyn40.csproj", "{C5AD8C4D-D731-4DDD-96BD-A6776A1FF20E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediator.SourceGenerator.Implementation", "src\Mediator.SourceGenerator.Implementation\Mediator.SourceGenerator.Implementation.csproj", "{22984D49-8DDF-4263-8375-D018643E44B2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mediator.SourceGenerator.Implementation", "src\Mediator.SourceGenerator.Implementation\Mediator.SourceGenerator.Implementation.csproj", "{22984D49-8DDF-4263-8375-D018643E44B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediator.SourceGenerator.Roslyn40.Tests", "test\Mediator.SourceGenerator.Roslyn40.Tests\Mediator.SourceGenerator.Roslyn40.Tests.csproj", "{E4EE80C5-179C-4483-9E91-3107B3E1CD5A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mediator.SourceGenerator.Roslyn40.Tests", "test\Mediator.SourceGenerator.Roslyn40.Tests\Mediator.SourceGenerator.Roslyn40.Tests.csproj", "{E4EE80C5-179C-4483-9E91-3107B3E1CD5A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediator.SmokeTestConsole", "test\Mediator.SmokeTestConsole\Mediator.SmokeTestConsole.csproj", "{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -322,6 +324,18 @@ Global
{E4EE80C5-179C-4483-9E91-3107B3E1CD5A}.Release|x64.Build.0 = Release|Any CPU
{E4EE80C5-179C-4483-9E91-3107B3E1CD5A}.Release|x86.ActiveCfg = Release|Any CPU
{E4EE80C5-179C-4483-9E91-3107B3E1CD5A}.Release|x86.Build.0 = Release|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Debug|x64.ActiveCfg = Debug|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Debug|x64.Build.0 = Debug|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Debug|x86.ActiveCfg = Debug|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Debug|x86.Build.0 = Debug|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Release|Any CPU.Build.0 = Release|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Release|x64.ActiveCfg = Release|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Release|x64.Build.0 = Release|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Release|x86.ActiveCfg = Release|Any CPU
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -348,6 +362,7 @@ Global
{C5AD8C4D-D731-4DDD-96BD-A6776A1FF20E} = {438C928E-FBB5-45B2-9CD2-7ED3341AC75E}
{22984D49-8DDF-4263-8375-D018643E44B2} = {438C928E-FBB5-45B2-9CD2-7ED3341AC75E}
{E4EE80C5-179C-4483-9E91-3107B3E1CD5A} = {ED1809FC-733B-4D9E-8FEE-70D3BB0BBD84}
{65D8C0F8-E4CF-452D-86FB-482FE63C7C89} = {ED1809FC-733B-4D9E-8FEE-70D3BB0BBD84}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D45B5457-4190-49B6-BF89-7FA5F4C8ABE2}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ internal sealed class CompilationAnalyzer

public bool ServiceLifetimeIsTransient => ServiceLifetimeSymbol.Name == "Transient";

public bool IsTestRun => (_context.Compilation.AssemblyName?.StartsWith("Mediator.Tests") ?? false) ||
(_context.Compilation.AssemblyName?.StartsWith("Mediator.SmokeTest") ?? false);

public CompilationAnalyzer(in CompilationAnalyzerContext context)
{
_context = context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ namespace {{ MediatorNamespace }}
{
private readonly global::System.IServiceProvider _sp;
{{~ if ServiceLifetimeIsSingleton ~}}
private FastLazyValue<DICache> _diCacheLazy;
{{ if IsTestRun }}internal{{ else }}private{{ end }} FastLazyValue<DICache> _diCacheLazy;
{{~ else ~}}
private DICache _diCache;
{{ if IsTestRun }}internal{{ else }}private{{ end }} DICache _diCache;
{{~ end ~}}

/// <summary>
Expand All @@ -178,16 +178,18 @@ namespace {{ MediatorNamespace }}
? (s => ((object[])s).Length) : (s => s.Count());
}

private struct FastLazyValue<T>
{{ if IsTestRun }}internal{{ else }}private{{ end }} struct FastLazyValue<T>
where T : struct
{
private const long UNINIT = 0;
private const long INITING = 1;
private const long INITD = 2;
{{ if IsTestRun }}internal{{ else }}private{{ end }} const long UNINIT = 0;
{{ if IsTestRun }}internal{{ else }}private{{ end }} const long INITING = 1;
{{ if IsTestRun }}internal{{ else }}private{{ end }} const long INITD = 2;
{{ if IsTestRun }}internal const long INVALID = -1;{{ end }}
{{ if IsTestRun }}internal const long CACHED = 3;{{ end }}

private global::System.Func<T> _generator;
private long _state;
private T _value;
{{ if IsTestRun }}internal{{ else }}private{{ end }} global::System.Func<T> _generator;
{{ if IsTestRun }}internal{{ else }}private{{ end }} long _state;
{{ if IsTestRun }}internal{{ else }}private{{ end }} T _value;

public T Value
{
Expand All @@ -201,7 +203,7 @@ namespace {{ MediatorNamespace }}
}
}

private T ValueSlow
{{ if IsTestRun }}internal{{ else }}private{{ end }} T ValueSlow
{
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
get
Expand All @@ -228,6 +230,47 @@ namespace {{ MediatorNamespace }}
}
}

{{~ if IsTestRun ~}}
internal (T, long) ValueInstrumented
{
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
get
{
if (_state != INITD)
return ValueSlowInstrumented;

return (_value, CACHED);
}
}

internal (T, long) ValueSlowInstrumented
{
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
get
{
var prevState = global::System.Threading.Interlocked.CompareExchange(ref _state, INITING, UNINIT);
switch (prevState)
{
case INITD:
// Someone has already completed init
return (_value, INITD);
case INITING:
// Wait for someone else to complete
var spinWait = default(global::System.Threading.SpinWait);
while (global::System.Threading.Interlocked.Read(ref _state) < INITD)
spinWait.SpinOnce();
return (_value, INITING);
case UNINIT:
_value = _generator();
global::System.Threading.Interlocked.Exchange(ref _state, INITD);
return (_value, UNINIT);
}

return (_value, INVALID);
}
}
{{~ end ~}}

public FastLazyValue(global::System.Func<T> generator)
{
_generator = generator;
Expand All @@ -236,7 +279,7 @@ namespace {{ MediatorNamespace }}
}
}

private readonly struct DICache
{{ if IsTestRun }}internal{{ else }}private{{ end }} readonly struct DICache
{
private readonly global::System.IServiceProvider _sp;

Expand Down
24 changes: 24 additions & 0 deletions test/Mediator.SmokeTestConsole/Mediator.SmokeTestConsole.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>

<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(DotNetVersion)" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="$(DotNetVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="xunit.assert" Version="2.4.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Mediator.SourceGenerator.Implementation\Mediator.SourceGenerator.Implementation.csproj" OutputItemType="Analyzer" />
<ProjectReference Include="..\..\src\Mediator.SourceGenerator.Roslyn40\Mediator.SourceGenerator.Roslyn40.csproj" OutputItemType="Analyzer" />
<ProjectReference Include="..\..\src\Mediator\Mediator.csproj" />
</ItemGroup>

</Project>
131 changes: 131 additions & 0 deletions test/Mediator.SmokeTestConsole/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using Mediator;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using DICache = Mediator.Mediator.DICache;
using LazyDICache = Mediator.Mediator.FastLazyValue<Mediator.Mediator.DICache>;

await Host.CreateDefaultBuilder()
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
logging.AddSimpleConsole(options =>
{
options.SingleLine = true;
});
})
.ConfigureServices(services =>
{
services.AddHostedService<Work>();
})
.Build()
.RunAsync();

public sealed class Work : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var concurrency = Environment.ProcessorCount;
var threads = new Task<(DICache Cache, long State)>[concurrency];

var services = new ServiceCollection();
services.AddMediator();

var iteration = 0;
const int maxIterations = 1_000_000;

Console.WriteLine(
$"Starting smoketests - " +
$"{nameof(concurrency)}={concurrency}" +
$", {nameof(maxIterations)}={maxIterations}"
);

while (!stoppingToken.IsCancellationRequested && iteration < maxIterations)
{
await using var sp = services.BuildServiceProvider(validateScopes: true);

var mediator = sp.GetRequiredService<Mediator.Mediator>();

var start = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

for (int i = 0; i < concurrency; i++)
threads[i] = Task.Run(Thread);

start.SetResult();
var values = await Task.WhenAll(threads).ConfigureAwait(false);
var states = values.Select(v => v.State).ToArray();
var firstHandlers = values.Select(v => v.Cache.Wrapper_For_Request).ToArray();
var firstHandler = firstHandlers[0];
var lastHandlers = values.Select(v => v.Cache.Wrapper_For_Request5).ToArray();
var lastHandler = lastHandlers[0];

var hasInvalidHandler = firstHandlers.Any(h => !ReferenceEquals(h, firstHandler)) ||
lastHandlers.Any(h => !ReferenceEquals(h, lastHandler));
var hasInvalid = states.Any(s => s == LazyDICache.INVALID);
var wasUninitCount = states.Count(s => s == LazyDICache.UNINIT);
var wasInitingCount = states.Count(s => s == LazyDICache.INITING);
var wasInitedCount = states.Count(s => s == LazyDICache.INITD);
var wasCachedCount = states.Count(s => s == LazyDICache.CACHED);


Console.WriteLine(
$"Ran smoketest iteration {++iteration} - " +
$"{nameof(hasInvalidHandler)}={hasInvalidHandler}" +
$", {nameof(hasInvalid)}={hasInvalid}" +
$", {nameof(wasUninitCount)}={wasUninitCount}" +
$", {nameof(wasInitingCount)}={wasInitingCount}" +
$", {nameof(wasInitedCount)}={wasInitedCount}" +
$", {nameof(wasCachedCount)}={wasCachedCount}"
);

if (hasInvalidHandler || hasInvalid || wasUninitCount != 1)
{
Console.WriteLine("Error condition, exiting...");
break;
}

async Task<(DICache Cache, long State)> Thread()
{
await start.Task.ConfigureAwait(false);

return mediator._diCacheLazy.ValueInstrumented;
}
}

Console.WriteLine("------------------");
Console.WriteLine(
$"Done smoketesting! - " +
$"{nameof(concurrency)}={concurrency}, {nameof(maxIterations)}={maxIterations}"
);
}
}

public sealed record Request() : IRequest;
public sealed class RequestHandler : IRequestHandler<Request>
{
public ValueTask<Unit> Handle(Request request, CancellationToken cancellationToken) => default;
}

public sealed record Request2() : IRequest;
public sealed class Request2Handler : IRequestHandler<Request2>
{
public ValueTask<Unit> Handle(Request2 request, CancellationToken cancellationToken) => default;
}

public sealed record Request3() : IRequest;
public sealed class Request3Handler : IRequestHandler<Request3>
{
public ValueTask<Unit> Handle(Request3 request, CancellationToken cancellationToken) => default;
}

public sealed record Request4() : IRequest;
public sealed class Request4Handler : IRequestHandler<Request4>
{
public ValueTask<Unit> Handle(Request4 request, CancellationToken cancellationToken) => default;
}

public sealed record Request5() : IRequest;
public sealed class Request5Handler : IRequestHandler<Request5>
{
public ValueTask<Unit> Handle(Request5 request, CancellationToken cancellationToken) => default;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<IsPackable>false</IsPackable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -33,7 +34,7 @@
</ItemGroup>

<ItemGroup>
<Compile Include="..\Mediator.Tests\**\*.cs" Exclude="..\Mediator.Tests\obj\**;..\Mediator.Tests\bin\**;..\Mediator.Tests\SingletonLifetimeTests.cs">
<Compile Include="..\Mediator.Tests\**\*.cs" Exclude="..\Mediator.Tests\obj\**;..\Mediator.Tests\bin\**;..\Mediator.Tests\SingletonLifetimeTests.cs;..\Mediator.Tests\SmokeTests.FastLazy.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<IsPackable>false</IsPackable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -33,7 +34,7 @@
</ItemGroup>

<ItemGroup>
<Compile Include="..\Mediator.Tests\**\*.cs" Exclude="..\Mediator.Tests\obj\**;..\Mediator.Tests\bin\**;..\Mediator.Tests\SingletonLifetimeTests.cs">
<Compile Include="..\Mediator.Tests\**\*.cs" Exclude="..\Mediator.Tests\obj\**;..\Mediator.Tests\bin\**;..\Mediator.Tests\SingletonLifetimeTests.cs;..\Mediator.Tests\SmokeTests.FastLazy.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
</ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions test/Mediator.Tests/Mediator.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<IsPackable>false</IsPackable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<!--<ReportAnalyzer>true</ReportAnalyzer>-->
</PropertyGroup>

Expand Down
2 changes: 0 additions & 2 deletions test/Mediator.Tests/SenderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

namespace Mediator.Tests;



public sealed class SenderTests
{
[Fact]
Expand Down
Loading