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)

Reply via email to