mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-26 08:02:59 +08:00
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:
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user