3
3
using Serilog . Sinks . XUnit . Injectable . Abstract ;
4
4
using System ;
5
5
using System . Collections . Generic ;
6
- using System . Threading ;
7
6
using System . Threading . Channels ;
8
7
using System . Threading . Tasks ;
8
+ using Soenneker . Extensions . Task ;
9
+ using Soenneker . Extensions . ValueTask ;
10
+ using Soenneker . Utils . AtomicBool ;
9
11
using Soenneker . Utils . ReusableStringWriter ;
10
12
using Xunit ;
11
13
using Xunit . Sdk ;
14
16
namespace Serilog . Sinks . XUnit . Injectable ;
15
17
16
18
/// <inheritdoc cref="IInjectableTestOutputSink"/>
17
- public sealed class InjectableTestOutputSink : IInjectableTestOutputSink
19
+ public sealed class InjectableTestOutputSink : IInjectableTestOutputSink , IAsyncDisposable , IDisposable
18
20
{
19
21
private const string _defaultTemplate = "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{Exception}" ;
20
22
21
23
private readonly MessageTemplateTextFormatter _fmt ;
22
24
23
25
private readonly Channel < LogEvent > _ch = Channel . CreateUnbounded < LogEvent > ( new UnboundedChannelOptions
24
- { SingleReader = true , SingleWriter = false , AllowSynchronousContinuations = false } ) ;
26
+ { SingleReader = true , SingleWriter = false , AllowSynchronousContinuations = false } ) ;
25
27
26
28
private readonly Task _readerTask ;
27
29
@@ -34,32 +36,31 @@ public sealed class InjectableTestOutputSink : IInjectableTestOutputSink
34
36
// Only the reader touches this; safe without locks.
35
37
private readonly Queue < LogEvent > _pending = new ( ) ;
36
38
37
- private int _disposed ;
39
+ private readonly AtomicBool _disposed = new ( ) ;
38
40
39
41
public InjectableTestOutputSink ( string outputTemplate = _defaultTemplate , IFormatProvider ? formatProvider = null )
40
42
{
41
43
_fmt = new MessageTemplateTextFormatter ( outputTemplate , formatProvider ) ;
42
-
43
44
_readerTask = Task . Run ( ReadLoop ) ;
44
45
}
45
46
46
47
public void Inject ( ITestOutputHelper helper , IMessageSink ? sink = null )
47
48
{
48
49
ArgumentNullException . ThrowIfNull ( helper ) ;
49
-
50
50
_helper = helper ;
51
51
_sink = sink ;
52
52
}
53
53
54
54
public void Emit ( LogEvent logEvent )
55
55
{
56
- if ( Volatile . Read ( ref _disposed ) == 0 )
56
+ if ( _disposed . IsFalse )
57
57
_ch . Writer . TryWrite ( logEvent ) ;
58
58
}
59
59
60
60
private async Task ReadLoop ( )
61
61
{
62
- await foreach ( LogEvent evt in _ch . Reader . ReadAllAsync ( ) . ConfigureAwait ( false ) )
62
+ await foreach ( LogEvent evt in _ch . Reader . ReadAllAsync ( )
63
+ . ConfigureAwait ( false ) )
63
64
{
64
65
ITestOutputHelper ? helper = _helper ; // volatile read
65
66
@@ -69,7 +70,7 @@ private async Task ReadLoop()
69
70
continue ;
70
71
}
71
72
72
- // first, flush any backlog that accumulated pre‑ inject
73
+ // first, flush any backlog that accumulated pre- inject
73
74
while ( _pending . Count > 0 )
74
75
{
75
76
Write ( _pending . Dequeue ( ) , helper ) ;
@@ -95,22 +96,42 @@ private void Write(LogEvent evt, ITestOutputHelper helper)
95
96
{
96
97
// Helper became invalid (test finished) – cache the event
97
98
_helper = null ;
98
-
99
99
_pending . Enqueue ( evt ) ;
100
100
}
101
101
}
102
102
103
103
public async ValueTask DisposeAsync ( )
104
104
{
105
- if ( Interlocked . Exchange ( ref _disposed , 1 ) != 0 )
105
+ if ( ! _disposed . TrySetTrue ( ) )
106
106
return ;
107
107
108
108
// 1) Tell the reader no more items are coming
109
109
_ch . Writer . TryComplete ( ) ;
110
110
111
111
// 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
+ }
113
134
114
- await _sw . DisposeAsync ( ) . ConfigureAwait ( false ) ;
135
+ _sw . Dispose ( ) ;
115
136
}
116
137
}
0 commit comments