This is an automated email from the ASF dual-hosted git repository. FreeAndNil pushed a commit to branch Feature/305-dotnet-10-build-cleanup in repository https://gitbox.apache.org/repos/asf/logging-log4net.git
commit bc298db91ae8b45b0f2e7b2f69981d3d64394187 Author: Jan Friedrich <[email protected]> AuthorDate: Tue Aug 4 23:37:41 2026 +0200 Quieten the TelnetAppender test Dispose disposed the TcpClient while the reader was blocked in stream.Read, so teardown always aborted the socket and dumped an IOException with a stack trace, even on a passing run. Shut the socket down first, so the read returns 0 and the loop ends normally, and only report exceptions that arrive before disposal. Progress chatter removed; the test now prints nothing unless it fails. Two things this fixes beyond the noise: - The cancellation token never actually stopped the loop. The check ran only after a successful read, so the reader could only be broken by disposing the socket under it. Now Shutdown(SocketShutdown.Both) ends the stream and the loop exits through its normal condition. - Diagnostics are preserved for real failures. log is still wired to TestContext.Out.WriteLine, gated on !_disposing - so a genuine client error still surfaces, and Assert.Fail on timeout still reports what was received. --- .../Appender/Internal/SimpleTelnetClient.cs | 47 +++++++++++++++------- src/log4net.Tests/Appender/TelnetAppenderTest.cs | 5 --- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/log4net.Tests/Appender/Internal/SimpleTelnetClient.cs b/src/log4net.Tests/Appender/Internal/SimpleTelnetClient.cs index 38f104ff..bc13c8c9 100644 --- a/src/log4net.Tests/Appender/Internal/SimpleTelnetClient.cs +++ b/src/log4net.Tests/Appender/Internal/SimpleTelnetClient.cs @@ -38,59 +38,78 @@ internal sealed class SimpleTelnetClient( { private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly TcpClient _client = new(); + private volatile bool _disposing; /// <summary> /// Runs the client (in a task) /// </summary> + /// <param name="log">Callback for unexpected errors - a passing run stays silent</param> internal void Run(Action<string> log) => Task.Run(() => { try { - log("client: starting ..."); _client.Connect(new IPEndPoint(IPAddress.Loopback, port)); - log("client: connected"); // Get a stream object for reading and writing using NetworkStream stream = _client.GetStream(); - log("client: has stream"); int i; byte[] bytes = new byte[256]; - // Loop to receive all the data sent by the server + // Loop to receive all the data sent by the server. Dispose shuts the socket down, + // which ends the stream, so this read returns 0 and the loop exits without throwing. while ((i = stream.Read(bytes, 0, bytes.Length)) != 0) { - string data = System.Text.Encoding.ASCII.GetString(bytes, 0, i); - log("client: read: " + data); - received(data); + received(System.Text.Encoding.ASCII.GetString(bytes, 0, i)); if (_cancellationTokenSource.Token.IsCancellationRequested) { - log("client: canceled"); return; } } - log("client: end of stream"); } // The test asserts on the received data, so a failing client must not end up - // as an unobserved task exception - log it instead. + // as an unobserved task exception - log it instead. Anything thrown once Dispose + // has started is teardown noise, so only genuine failures reach the output. catch (SocketException e) { - log("client: error: " + e); + Report(e); } catch (IOException e) { - log("client: error: " + e); + Report(e); } catch (ObjectDisposedException e) { - // expected when the client is disposed while reading - log("client: disposed: " + e.Message); + Report(e); + } + + void Report(Exception e) + { + if (!_disposing) + { + log("client: error: " + e); + } } }, _cancellationTokenSource.Token); /// <inheritdoc/> public void Dispose() { + _disposing = true; _cancellationTokenSource.Cancel(); + // Shut the socket down before disposing it: that ends the stream cleanly, so a read + // blocked in Run returns 0 instead of failing with a connection abort. + try + { + _client.Client?.Shutdown(SocketShutdown.Both); + } + catch (SocketException) + { + // not connected - nothing to shut down + } + catch (ObjectDisposedException) + { + // already disposed - nothing to shut down + } _cancellationTokenSource.Dispose(); _client.Dispose(); } diff --git a/src/log4net.Tests/Appender/TelnetAppenderTest.cs b/src/log4net.Tests/Appender/TelnetAppenderTest.cs index 9596f065..e8cf95ec 100644 --- a/src/log4net.Tests/Appender/TelnetAppenderTest.cs +++ b/src/log4net.Tests/Appender/TelnetAppenderTest.cs @@ -86,15 +86,11 @@ public void TelnetTest() { using (SimpleTelnetClient telnetClient = new(Received, port)) { - TestContext.Out.WriteLine("test: starting client ..."); telnetClient.Run(TestContext.Out.WriteLine); WaitForReceived("welcome message", WelcomeMessage); ILogger logger = repository.GetLogger("Telnet"); - TestContext.Out.WriteLine("test: logging to client ..."); logger.Log(typeof(TelnetAppenderTest), Level.Info, logId, null); - TestContext.Out.WriteLine("test: waiting for message of client ..."); WaitForReceived("log message", logId); - TestContext.Out.WriteLine("test: canceling client ..."); } } finally @@ -130,7 +126,6 @@ void WaitForReceived(string what, string expected) } Thread.Sleep(20); } - TestContext.Out.WriteLine($"receiver: received {what} after {stopwatch.ElapsedMilliseconds} ms"); } }
