This is an automated email from the ASF dual-hosted git repository. FreeAndNil pushed a commit to branch Feature/AVSS-Fixes in repository https://gitbox.apache.org/repos/asf/logging-log4net.git
commit 046332dc532568053f350a11271ac09b3d79e126 Author: Jan Friedrich <[email protected]> AuthorDate: Mon Jun 22 00:14:51 2026 +0200 added unit tests --- src/log4net.Tests/Appender/AppenderSkeletonTest.cs | 88 ++++++++++++++++++++++ src/log4net.Tests/Appender/FileAppenderTest.cs | 31 ++++++++ src/log4net/Appender/AppenderSkeleton.cs | 5 +- 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/log4net.Tests/Appender/AppenderSkeletonTest.cs b/src/log4net.Tests/Appender/AppenderSkeletonTest.cs new file mode 100644 index 00000000..ca41386a --- /dev/null +++ b/src/log4net.Tests/Appender/AppenderSkeletonTest.cs @@ -0,0 +1,88 @@ +#region Apache License +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to you under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +using log4net.Filter; +using log4net.Appender; +using NUnit.Framework; + +namespace log4net.Tests.Appender; + +/// <summary> +/// Unit tests for <see cref="AppenderSkeleton"/> filter chain management. +/// </summary> +[TestFixture] +public sealed class AppenderSkeletonTest +{ + private CountingAppender _appender = null!; + + [SetUp] + public void SetUp() => _appender = new CountingAppender(); + + [TearDown] + public void TearDown() => _appender.Close(); + + /// <summary> + /// Verifies that <see cref="AppenderSkeleton.AddFilter"/> sets <see cref="AppenderSkeleton.FilterHead"/> when the chain is empty. + /// </summary> + [Test] + public void AddFilter_FirstFilter_SetsFilterHead() + { + DenyAllFilter filter = new(); + _appender.AddFilter(filter); + Assert.That(_appender.FilterHead, Is.SameAs(filter)); + } + + /// <summary> + /// Verifies that a second <see cref="AppenderSkeleton.AddFilter"/> call appends the filter via <see cref="Filter.IFilter.Next"/>. + /// </summary> + [Test] + public void AddFilter_SecondFilter_LinksToChain() + { + DenyAllFilter first = new(); + DenyAllFilter second = new(); + _appender.AddFilter(first); + _appender.AddFilter(second); + Assert.That(_appender.FilterHead, Is.SameAs(first)); + Assert.That(_appender.FilterHead!.Next, Is.SameAs(second)); + } + + /// <summary> + /// Verifies that <see cref="AppenderSkeleton.ClearFilters"/> resets <see cref="AppenderSkeleton.FilterHead"/> to null. + /// </summary> + [Test] + public void ClearFilters_ResetsFilterHead() + { + _appender.AddFilter(new DenyAllFilter()); + _appender.ClearFilters(); + Assert.That(_appender.FilterHead, Is.Null); + } + + /// <summary> + /// Verifies that filters can be added again after <see cref="AppenderSkeleton.ClearFilters"/>. + /// </summary> + [Test] + public void ClearFilters_AllowsAddingFiltersAgain() + { + _appender.AddFilter(new DenyAllFilter()); + _appender.ClearFilters(); + DenyAllFilter filter = new(); + _appender.AddFilter(filter); + Assert.That(_appender.FilterHead, Is.SameAs(filter)); + } +} diff --git a/src/log4net.Tests/Appender/FileAppenderTest.cs b/src/log4net.Tests/Appender/FileAppenderTest.cs index 9cebdcef..8c11af35 100644 --- a/src/log4net.Tests/Appender/FileAppenderTest.cs +++ b/src/log4net.Tests/Appender/FileAppenderTest.cs @@ -29,6 +29,8 @@ using log4net.Core; using System.IO; using System.Linq; +using System.Text; +using System.Threading.Tasks; namespace log4net.Tests.Appender; @@ -151,4 +153,33 @@ public void FilenameWithGlobalContextPatternStringTest() logs.Refresh(); Assert.That(logs.GetFiles().Any(file => file.Name.StartsWith("file_custom_log_issue_193"))); } + + /// <summary> + /// Verifies that <see cref="FileAppender.InterProcessLock"/> releases the mutex + /// when the underlying file stream is null. + /// </summary> + [Test] + public void InterProcessLock_AcquireLock_ReleasesMutexWhenStreamIsNull() + { + FileAppender appender = new() { File = "log4net_ipl_test" }; + FileAppender.InterProcessLock lockingModel = new() { CurrentAppender = appender }; + lockingModel.ActivateOptions(); + // null byte is illegal in paths on all platforms; OpenFile catches the exception and _stream stays null + const string invalidPath = "/tmp/invalid\0/test.log"; + lockingModel.OpenFile(invalidPath, false, Encoding.UTF8); + try + { + Stream? stream = lockingModel.AcquireLock(); + Assert.That(stream, Is.Null); + + // if the mutex was released, a second thread can acquire the lock without blocking + Task task = Task.Run(lockingModel.AcquireLock); + Assert.That(task.Wait(TimeSpan.FromSeconds(2)), Is.True, + "Mutex was not released by AcquireLock when stream is null"); + } + finally + { + lockingModel.OnClose(); + } + } } \ No newline at end of file diff --git a/src/log4net/Appender/AppenderSkeleton.cs b/src/log4net/Appender/AppenderSkeleton.cs index adf34128..fb0987b1 100644 --- a/src/log4net/Appender/AppenderSkeleton.cs +++ b/src/log4net/Appender/AppenderSkeleton.cs @@ -119,6 +119,7 @@ public virtual IErrorHandler ErrorHandler { lock (LockObj) { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (value is null) { // We do not throw exception here since the cause is probably a @@ -262,7 +263,7 @@ public void Close() public void DoAppend(LoggingEvent loggingEvent) { // This lock is absolutely critical for correct formatting - // of the message in a multi-threaded environment. Without + // of the message in a multithreaded environment. Without // this, the message may be broken up into elements from // multiple thread contexts (like get the wrong thread ID). @@ -346,7 +347,7 @@ public void DoAppend(LoggingEvent[] loggingEvents) loggingEvents.EnsureNotNull(); // This lock is absolutely critical for correct formatting - // of the message in a multi-threaded environment. Without + // of the message in a multithreaded environment. Without // this, the message may be broken up into elements from // multiple thread contexts (like get the wrong thread ID). lock (LockObj)
