Use latest language features (#2176)

* Make use of language features line AsyncDispose, init etc.

* Fix build errors

* Improve Unit Tests

* Improve Unit Tests

* Improve Unit Tests

* Update samples

* Remove obsolete benchmarks

* Code cleanup

* Improve unit tests

* Improve Unit Tests

* Fix Unit Tests

* Improve Unit Tests

* Improve Unit Tests

* Restore unit tests

* Exclude load tests from unit tests
This commit is contained in:
Christian
2025-08-30 10:52:59 +02:00
committed by GitHub
parent 247988e633
commit e43e7f8e36
330 changed files with 21417 additions and 22919 deletions
@@ -4,24 +4,23 @@
using System.IO.Pipelines;
namespace MQTTnet.Tests.ASP.Mockups
namespace MQTTnet.Tests.ASP.Mockups;
public sealed class DuplexPipeMockup : IDuplexPipe
{
public sealed class DuplexPipeMockup : IDuplexPipe
public DuplexPipeMockup()
{
public DuplexPipeMockup()
{
var pool = new LimitedMemoryPool();
var pipeOptions = new PipeOptions(pool);
Receive = new Pipe(pipeOptions);
Send = new Pipe(pipeOptions);
}
PipeReader IDuplexPipe.Input => Receive.Reader;
PipeWriter IDuplexPipe.Output => Send.Writer;
public Pipe Receive { get; }
public Pipe Send { get; }
var pool = new LimitedMemoryPool();
var pipeOptions = new PipeOptions(pool);
Receive = new Pipe(pipeOptions);
Send = new Pipe(pipeOptions);
}
}
PipeReader IDuplexPipe.Input => Receive.Reader;
PipeWriter IDuplexPipe.Output => Send.Writer;
public Pipe Receive { get; }
public Pipe Send { get; }
}
@@ -4,19 +4,18 @@
using System.Buffers;
namespace MQTTnet.Tests.ASP.Mockups
namespace MQTTnet.Tests.ASP.Mockups;
public sealed class LimitedMemoryPool : MemoryPool<byte>
{
public sealed class LimitedMemoryPool : MemoryPool<byte>
protected override void Dispose(bool disposing)
{
protected override void Dispose(bool disposing)
{
}
public override IMemoryOwner<byte> Rent(int minBufferSize = -1)
{
return new MemoryOwner(minBufferSize);
}
public override int MaxBufferSize { get; }
}
public override IMemoryOwner<byte> Rent(int minBufferSize = -1)
{
return new MemoryOwner(minBufferSize);
}
public override int MaxBufferSize { get; } = 1;
}
+20 -21
View File
@@ -5,33 +5,32 @@
using System;
using System.Buffers;
namespace MQTTnet.Tests.ASP.Mockups
namespace MQTTnet.Tests.ASP.Mockups;
public sealed class MemoryOwner : IMemoryOwner<byte>
{
public sealed class MemoryOwner : IMemoryOwner<byte>
readonly byte[] _raw;
public MemoryOwner(int size)
{
readonly byte[] _raw;
public MemoryOwner(int size)
if (size <= 0)
{
if (size <= 0)
{
size = 1024;
}
if (size > 4096)
{
size = 4096;
}
_raw = ArrayPool<byte>.Shared.Rent(size);
Memory = _raw;
size = 1024;
}
public Memory<byte> Memory { get; }
public void Dispose()
if (size > 4096)
{
ArrayPool<byte>.Shared.Return(_raw);
size = 4096;
}
_raw = ArrayPool<byte>.Shared.Rent(size);
Memory = _raw;
}
public Memory<byte> Memory { get; }
public void Dispose()
{
ArrayPool<byte>.Shared.Return(_raw);
}
}
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
@@ -17,114 +16,113 @@ using MQTTnet.Protocol;
using MQTTnet.Tests.ASP.Mockups;
using MQTTnet.Tests.Helpers;
namespace MQTTnet.Tests.ASP
namespace MQTTnet.Tests.ASP;
[TestClass]
public class MqttConnectionContextTest
{
[TestClass]
public class MqttConnectionContextTest
[TestMethod]
public async Task TestCorruptedConnectPacket()
{
[TestMethod]
public async Task TestCorruptedConnectPacket()
var writer = new MqttBufferWriter(4096, 65535);
var serializer = new MqttPacketFormatterAdapter(writer);
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, []));
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None)).ConfigureAwait(false);
}
// TODO: Fix test
// [TestMethod]
// public async Task TestEndpoint()
// {
// var mockup = new ConnectionHandlerMockup();
//
// using (var host = new WebHostBuilder().UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
// .UseStartup<Startup>()
// .ConfigureServices(
// (hostContext, services) =>
// {
// services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
// services.AddSingleton<IMqttServerAdapter>(mockup);
// })
// .Build())
//
// using (var client = new MqttFactory().CreateMqttClient())
// {
// host.Start();
// await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build(), CancellationToken.None);
//
// var ctx = await mockup.Context.Task;
//
// var ep = IPEndPoint.Parse(ctx.Endpoint);
// Assert.IsNotNull(ep);
//
// Assert.IsNotNull(ctx);
// }
// }
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
//[TestMethod]
//public async Task TestParallelWrites()
//{
// var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
// var pipe = new DuplexPipeMockup();
// var connection = new DefaultConnectionContext();
// connection.Transport = pipe;
// var ctx = new MqttConnectionContext(serializer, connection);
// var tasks = Enumerable.Range(1, 100).Select(_ => Task.Run(async () =>
// {
// for (int i = 0; i < 100; i++)
// {
// await ctx.SendPacketAsync(new MqttPublishPacket(), TimeSpan.Zero, CancellationToken.None).ConfigureAwait(false);
// }
// }));
// await Task.WhenAll(tasks).ConfigureAwait(false);
//}
[TestMethod]
public async Task TestLargePacket()
{
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
await ctx.SendPacketAsync(new MqttPublishPacket { PayloadSegment = new byte[20_000] }, CancellationToken.None).ConfigureAwait(false);
var readResult = await pipe.Send.Reader.ReadAsync();
Assert.IsTrue(readResult.Buffer.Length > 20000);
}
[TestMethod]
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
{
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
await pipe.Receive.Writer.CompleteAsync();
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None)).ConfigureAwait(false);
}
class Startup
{
public void Configure(IApplicationBuilder app)
{
var writer = new MqttBufferWriter(4096, 65535);
var serializer = new MqttPacketFormatterAdapter(writer);
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
await pipe.Receive.Writer.WriteAsync(writer.AddMqttHeader(MqttControlPacketType.Connect, Array.Empty<byte>()));
await Assert.ThrowsExceptionAsync<MqttProtocolViolationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
// the first exception should complete the pipes so if someone tries to use the connection after that it should throw immidiatly
await Assert.ThrowsExceptionAsync<InvalidOperationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
}
// TODO: Fix test
// [TestMethod]
// public async Task TestEndpoint()
// {
// var mockup = new ConnectionHandlerMockup();
//
// using (var host = new WebHostBuilder().UseKestrel(kestrel => kestrel.ListenLocalhost(1883, listener => listener.Use((ctx, next) => mockup.OnConnectedAsync(ctx))))
// .UseStartup<Startup>()
// .ConfigureServices(
// (hostContext, services) =>
// {
// services.AddHostedMqttServer(o => o.WithoutDefaultEndpoint());
// services.AddSingleton<IMqttServerAdapter>(mockup);
// })
// .Build())
//
// using (var client = new MqttFactory().CreateMqttClient())
// {
// host.Start();
// await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build(), CancellationToken.None);
//
// var ctx = await mockup.Context.Task;
//
// var ep = IPEndPoint.Parse(ctx.Endpoint);
// Assert.IsNotNull(ep);
//
// Assert.IsNotNull(ctx);
// }
// }
// COMMENTED OUT DUE TO DEAD LOCK? OR VERY VERY SLOW PERFORMANCE ON LOCAL DEV MACHINE. TEST WAS STILL RUNNING AFTER SEVERAL MINUTES!
//[TestMethod]
//public async Task TestParallelWrites()
//{
// var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
// var pipe = new DuplexPipeMockup();
// var connection = new DefaultConnectionContext();
// connection.Transport = pipe;
// var ctx = new MqttConnectionContext(serializer, connection);
// var tasks = Enumerable.Range(1, 100).Select(_ => Task.Run(async () =>
// {
// for (int i = 0; i < 100; i++)
// {
// await ctx.SendPacketAsync(new MqttPublishPacket(), TimeSpan.Zero, CancellationToken.None).ConfigureAwait(false);
// }
// }));
// await Task.WhenAll(tasks).ConfigureAwait(false);
//}
[TestMethod]
public async Task TestLargePacket()
{
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
await ctx.SendPacketAsync(new MqttPublishPacket { PayloadSegment = new byte[20_000] }, CancellationToken.None).ConfigureAwait(false);
var readResult = await pipe.Send.Reader.ReadAsync();
Assert.IsTrue(readResult.Buffer.Length > 20000);
}
[TestMethod]
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
{
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
pipe.Receive.Writer.Complete();
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(CancellationToken.None));
}
class Startup
{
public void Configure(IApplicationBuilder app)
{
}
}
}
}
@@ -22,25 +22,20 @@ public sealed class ReaderExtensionsTest
var sequence = new ReadOnlySequence<byte>(buffer.Array, buffer.Offset, buffer.Count);
var part = sequence;
var consumed = part.Start;
var observed = part.Start;
var read = 0;
part = sequence.Slice(sequence.Start, 0); // empty message should fail
var result = serializer.TryDecode(part, out _, out consumed, out observed, out read);
var part = sequence.Slice(sequence.Start, 0); // empty message should fail
var result = serializer.TryDecode(part, out _, out _, out _, out _);
Assert.IsFalse(result);
part = sequence.Slice(sequence.Start, 1); // partial fixed header should fail
result = serializer.TryDecode(part, out _, out consumed, out observed, out read);
result = serializer.TryDecode(part, out _, out _, out _, out _);
Assert.IsFalse(result);
part = sequence.Slice(sequence.Start, 4); // partial body should fail
result = serializer.TryDecode(part, out _, out consumed, out observed, out read);
result = serializer.TryDecode(part, out _, out _, out _, out _);
Assert.IsFalse(result);
part = sequence; // complete msg should work
result = serializer.TryDecode(part, out _, out consumed, out observed, out read);
result = serializer.TryDecode(part, out _, out _, out _, out _);
Assert.IsTrue(result);
}
}