Skip to content

Commit 630d1dd

Browse files
committed
Including regular disposal due to upstream callers..
1 parent 6c2d04c commit 630d1dd

8 files changed

+66
-14
lines changed

src/Serilog.Sinks.XUnit.Injectable/Abstract/IInjectableTestOutputSink.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Serilog.Sinks.XUnit.Injectable.Abstract;
1111
/// A sink to direct Serilog output to the XUnit test output via dependency injection <para/>
1212
/// Use as a Singleton
1313
/// </summary>
14-
public interface IInjectableTestOutputSink : ILogEventSink, IAsyncDisposable
14+
public interface IInjectableTestOutputSink : ILogEventSink, IAsyncDisposable, IDisposable
1515
{
1616
/// <summary>
1717
/// Call this as soon as you have a new instance of the testOutputHelper (usually at the beginning of a xUnit test

src/Serilog.Sinks.XUnit.Injectable/InjectableTestOutputSink.cs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
using Serilog.Sinks.XUnit.Injectable.Abstract;
44
using System;
55
using System.Collections.Generic;
6-
using System.Threading;
76
using System.Threading.Channels;
87
using System.Threading.Tasks;
8+
using Soenneker.Extensions.Task;
9+
using Soenneker.Extensions.ValueTask;
10+
using Soenneker.Utils.AtomicBool;
911
using Soenneker.Utils.ReusableStringWriter;
1012
using Xunit;
1113
using Xunit.Sdk;
@@ -14,14 +16,14 @@
1416
namespace Serilog.Sinks.XUnit.Injectable;
1517

1618
/// <inheritdoc cref="IInjectableTestOutputSink"/>
17-
public sealed class InjectableTestOutputSink : IInjectableTestOutputSink
19+
public sealed class InjectableTestOutputSink : IInjectableTestOutputSink, IAsyncDisposable, IDisposable
1820
{
1921
private const string _defaultTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{Exception}";
2022

2123
private readonly MessageTemplateTextFormatter _fmt;
2224

2325
private readonly Channel<LogEvent> _ch = Channel.CreateUnbounded<LogEvent>(new UnboundedChannelOptions
24-
{SingleReader = true, SingleWriter = false, AllowSynchronousContinuations = false});
26+
{ SingleReader = true, SingleWriter = false, AllowSynchronousContinuations = false });
2527

2628
private readonly Task _readerTask;
2729

@@ -34,32 +36,31 @@ public sealed class InjectableTestOutputSink : IInjectableTestOutputSink
3436
// Only the reader touches this; safe without locks.
3537
private readonly Queue<LogEvent> _pending = new();
3638

37-
private int _disposed;
39+
private readonly AtomicBool _disposed = new();
3840

3941
public InjectableTestOutputSink(string outputTemplate = _defaultTemplate, IFormatProvider? formatProvider = null)
4042
{
4143
_fmt = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
42-
4344
_readerTask = Task.Run(ReadLoop);
4445
}
4546

4647
public void Inject(ITestOutputHelper helper, IMessageSink? sink = null)
4748
{
4849
ArgumentNullException.ThrowIfNull(helper);
49-
5050
_helper = helper;
5151
_sink = sink;
5252
}
5353

5454
public void Emit(LogEvent logEvent)
5555
{
56-
if (Volatile.Read(ref _disposed) == 0)
56+
if (_disposed.IsFalse)
5757
_ch.Writer.TryWrite(logEvent);
5858
}
5959

6060
private async Task ReadLoop()
6161
{
62-
await foreach (LogEvent evt in _ch.Reader.ReadAllAsync().ConfigureAwait(false))
62+
await foreach (LogEvent evt in _ch.Reader.ReadAllAsync()
63+
.ConfigureAwait(false))
6364
{
6465
ITestOutputHelper? helper = _helper; // volatile read
6566

@@ -69,7 +70,7 @@ private async Task ReadLoop()
6970
continue;
7071
}
7172

72-
// first, flush any backlog that accumulated preinject
73+
// first, flush any backlog that accumulated pre-inject
7374
while (_pending.Count > 0)
7475
{
7576
Write(_pending.Dequeue(), helper);
@@ -95,22 +96,42 @@ private void Write(LogEvent evt, ITestOutputHelper helper)
9596
{
9697
// Helper became invalid (test finished) – cache the event
9798
_helper = null;
98-
9999
_pending.Enqueue(evt);
100100
}
101101
}
102102

103103
public async ValueTask DisposeAsync()
104104
{
105-
if (Interlocked.Exchange(ref _disposed, 1) != 0)
105+
if (!_disposed.TrySetTrue())
106106
return;
107107

108108
// 1) Tell the reader no more items are coming
109109
_ch.Writer.TryComplete();
110110

111111
// 2) Let the reader finish formatting & flushing
112-
await _readerTask.ConfigureAwait(false);
112+
await _readerTask.NoSync();
113+
114+
await _sw.DisposeAsync()
115+
.NoSync();
116+
}
117+
118+
public void Dispose()
119+
{
120+
if (!_disposed.TrySetTrue())
121+
return;
122+
123+
_ch.Writer.TryComplete();
124+
125+
try
126+
{
127+
_readerTask.GetAwaiter()
128+
.GetResult();
129+
}
130+
catch
131+
{
132+
// swallow during teardown
133+
}
113134

114-
await _sw.DisposeAsync().ConfigureAwait(false);
135+
_sw.Dispose();
115136
}
116137
}

src/Serilog.Sinks.XUnit.Injectable/Serilog.Sinks.XUnit.Injectable.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242

4343
<ItemGroup>
4444
<PackageReference Include="Serilog" Version="4.3.0" />
45+
<PackageReference Include="Soenneker.Extensions.Task" Version="3.0.105" />
46+
<PackageReference Include="Soenneker.Extensions.ValueTask" Version="3.0.95" />
47+
<PackageReference Include="Soenneker.Utils.AtomicBool" Version="3.0.8" />
4548
<PackageReference Include="Soenneker.Utils.ReusableStringWriter" Version="3.0.5" />
4649
<PackageReference Include="xunit.v3.extensibility.core" Version="3.0.1" />
4750
</ItemGroup>

test/Sinks/BlockingCollectionInjectableTestOutputSink.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,10 @@ public async ValueTask DisposeAsync()
115115

116116
_queue.Dispose();
117117
}
118+
119+
public void Dispose()
120+
{
121+
_queue.Dispose();
122+
_consumerTask.Dispose();
123+
}
118124
}

test/Sinks/ChannelInjectableTestOutputSink.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,11 @@ public async ValueTask DisposeAsync()
121121
await _sw.DisposeAsync().ConfigureAwait(false);
122122
}
123123
}
124+
125+
public void Dispose()
126+
{
127+
_readerTask.Dispose();
128+
_cts.Dispose();
129+
_sw.Dispose();
130+
}
124131
}

test/Sinks/ConcurrentInjectableTestOutputSink.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,9 @@ public ValueTask DisposeAsync()
9595
{
9696
return ValueTask.CompletedTask;
9797
}
98+
99+
public void Dispose()
100+
{
101+
// TODO release managed resources here
102+
}
98103
}

test/Sinks/OriginalInjectableTestOutputSink.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,9 @@ public ValueTask DisposeAsync()
9191
{
9292
return ValueTask.CompletedTask;
9393
}
94+
95+
public void Dispose()
96+
{
97+
// TODO release managed resources here
98+
}
9499
}

test/Sinks/QueueInjectableTestOutputSink.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,9 @@ public ValueTask DisposeAsync()
9191
{
9292
return ValueTask.CompletedTask;
9393
}
94+
95+
public void Dispose()
96+
{
97+
// TODO release managed resources here
98+
}
9499
}

0 commit comments

Comments
 (0)