Skip to content
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
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
<PackageVersion Include="WB.CodeAnalyzer" Version="1.0.0" />
<PackageVersion Include="AwesomeAssertions" Version="9.4.0" />
<PackageVersion Include="AwesomeAssertions.Analyzers" Version="9.0.8" />
<PackageVersion Include="FakeItEasy" Version="9.0.1" />
<PackageVersion Include="FakeItEasy.Analyzer.CSharp" Version="6.1.1" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.202" />
<PackageVersion Include="Microsoft.Testing.Platform" Version="2.0.2" />
<PackageVersion Include="TUnit" Version="1.33.0" />
Expand Down
21 changes: 19 additions & 2 deletions src/AsyncLogSinkBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public TWriter Writer
}
}
} = writer;

/// <summary>
/// Gets or sets a value indicating whether this sink is disabled.
/// </summary>
Expand All @@ -73,7 +73,7 @@ public TWriter Writer
public bool IsDisabled
{
get => Volatile.Read(ref isDisabled) == 1;
set => Interlocked.Exchange(ref isDisabled, value ? 1 : 0);
private set => Interlocked.Exchange(ref isDisabled, value ? 1 : 0);
}

// ┌─────────────────────────────────────────────────────────────────────────────┐
Expand Down Expand Up @@ -122,6 +122,23 @@ public IDisposable RegisterLogMessageWriter<TPayload>(IAsyncLogMessageWriter<TPa
return new DelegateDisposable(() => logMessageWriters.TryRemove(typeof(TPayload), out _));
}

/// <summary>
/// Disables this log sink, preventing it from processing any log messages until it is re-enabled.
/// </summary>
/// <returns>A <see cref="IDisposable"/> that, when disposed, re-enables the log sink.</returns>
/// <exception cref="InvalidOperationException">Thrown if the log sink is already disabled.</exception>
public IDisposable Disable()
{
if (IsDisabled)
{
throw new InvalidOperationException("The log sink is already disabled.");
}

IsDisabled = true;

return new DelegateDisposable(() => IsDisabled = false);
}

// ┌─────────────────────────────────────────────────────────────────────────────┐
// │ Private Methods │
// └─────────────────────────────────────────────────────────────────────────────┘
Expand Down
2 changes: 1 addition & 1 deletion src/LogMessageWriter/IHasWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ public interface IHasWriter<TWriter>
/// <summary>
/// Gets or sets the <typeparamref name="TWriter"/>.
/// </summary>
public TWriter Writer { get; set;}
public TWriter Writer { get; set; }
}
107 changes: 107 additions & 0 deletions tests/AsyncLogSinkBaseTests/src/MethodTests/DisableMethodTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AwesomeAssertions;
using FakeItEasy;
using WB.Logging;
using WB.Logging.LogSinks.Base;

namespace AsyncLogSinkBaseTests.MethodTests.DisableMethodTests;

internal sealed class TestWriter
{
}

internal sealed class TestLogSink() : AsyncLogSinkBase<TestWriter>(new TestLogMessageWriter(), new TestWriter())
{
}

internal sealed class TestLogMessageWriter : IAsyncLogMessageWriter<object, TestWriter>
{
public bool Called { get; private set; }

public TestWriter Writer { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

public IAsyncLogSink? LogSink { get; set; }

public ValueTask WriteAsync(DateTimeOffset timestamp, LogLevel? logLevel, IEnumerable<string> senders, object? payload)
{
Called = true;

return ValueTask.CompletedTask;
}
}

public sealed class TheDisableMethod
{
[Test]
public void ShouldSetIsDisabledToTrue()
{
// Arrange
TestLogSink logSink = new();

// Act
logSink.Disable();

// Assert
logSink.IsDisabled.Should().BeTrue(because: "calling Disable() should set IsDisabled to true");
}

[Test]
public void ShouldThrowInvalidOperationExceptionIfAlreadyDisabled()
{
// Arrange
TestLogSink logSink = new();

// Act
Action action = () =>
{
logSink.Disable();
logSink.Disable();
};

// Act & Assert
action.Should().Throw<InvalidOperationException>(because: "calling Disable() on an already disabled log sink should throw an InvalidOperationException");
}

[Test]
[Arguments(true, false, DisplayName = "when log sink is disabled")]
[Arguments(false, true, DisplayName = "when log sink is enabled")]
public async Task ShouldPreventProcessingLogMessages(bool isDisabled, bool logMessageWriterCalled)
{
// Arrange
TestLogMessageWriter logMessageWriter = new();
TestLogSink logSink = new();
logSink.RegisterLogMessageWriter(logMessageWriter);

if (isDisabled)
{
logSink.Disable();
}

ILogMessage<object> logMessage = A.Fake<ILogMessage<object>>();

// Act
await logSink.SubmitAsync(logMessage);

// Assert
logMessageWriter.Called.Should().Be(logMessageWriterCalled, because: "the log message writer should only be called if the log sink is not disabled");
}

[Test]
public void ShouldReEnableLogSinkWhenReturnedIDisposableIsDisposed()
{
// Arrange
TestLogSink logSink = new();

// Act
using (logSink.Disable())
{
// Assert
logSink.IsDisabled.Should().BeTrue(because: "the log sink should be disabled within the using block");
}

// Assert
logSink.IsDisabled.Should().BeFalse(because: "the log sink should be re-enabled after the using block");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,4 @@ public void ShouldReturnFalseByDefault()
// Assert
isDisabled.Should().BeFalse(because: "log sinks should be enabled by default");
}

[Test]
[Arguments(true, false)]
[Arguments(false, true)]
public async Task ShouldSetAndGetIsDisabledProperty(bool isDisabled, bool logMessageWriterCalled)
{
// Arrange
TestLogSink logSink = new()
{
IsDisabled = isDisabled
};

TestLogMessageWriter logMessageWriter = (TestLogMessageWriter)logSink.DefaultLogMessageWriter;

// Act
await logSink.SubmitAsync(new LogMessage<object>() { Payload = new object() }).ConfigureAwait(false);

// Assert
logMessageWriter.Called.Should().Be(logMessageWriterCalled, because: "the log message writer should only be called when the log sink is not disabled");
}
}
8 changes: 8 additions & 0 deletions tests/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@
<ItemGroup>
<PackageReference Include="AwesomeAssertions" />
<PackageReference Include="AwesomeAssertions.Analyzers" />
<PackageReference Include="FakeItEasy" />
<PackageReference Include="TUnit" />
<PackageReference Include="Verify" />
<PackageReference Include="Verify.TUnit" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FakeItEasy.Analyzer.CSharp">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

</Project>
Loading