mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-27 08:42:37 +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);
|
||||
}
|
||||
}
|
||||
@@ -8,21 +8,20 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
public abstract class BaseTestClass
|
||||
{
|
||||
public abstract class BaseTestClass
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
protected TestEnvironment CreateTestEnvironment(
|
||||
MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, bool trackUnobservedTaskException = true)
|
||||
{
|
||||
public TestContext TestContext { get; set; }
|
||||
return new TestEnvironment(TestContext, protocolVersion, trackUnobservedTaskException);
|
||||
}
|
||||
|
||||
protected TestEnvironment CreateTestEnvironment(
|
||||
MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, bool trackUnobservedTaskException = true)
|
||||
{
|
||||
return new TestEnvironment(TestContext, protocolVersion, trackUnobservedTaskException);
|
||||
}
|
||||
|
||||
protected Task LongTestDelay()
|
||||
{
|
||||
return Task.Delay(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
protected Task LongTestDelay()
|
||||
{
|
||||
return Task.Delay(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
}
|
||||
@@ -14,207 +14,194 @@ using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Clients.LowLevelMqttClient
|
||||
namespace MQTTnet.Tests.Clients.LowLevelMqttClient;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class LowLevelMqttClient_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class LowLevelMqttClient_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Authenticate()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Authenticate()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var factory = new MqttClientFactory();
|
||||
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
||||
|
||||
await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
|
||||
|
||||
var receivedPacket = await Authenticate(lowLevelClient).ConfigureAwait(false);
|
||||
|
||||
await lowLevelClient.DisconnectAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
Assert.IsNotNull(receivedPacket);
|
||||
Assert.AreEqual(MqttConnectReturnCode.ConnectionAccepted, receivedPacket.ReturnCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_And_Disconnect()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var lowLevelClient = testEnvironment.ClientFactory.CreateLowLevelMqttClient();
|
||||
|
||||
await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
|
||||
|
||||
await lowLevelClient.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Not_Existing_Broker()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateLowLevelMqttClient();
|
||||
var options = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
|
||||
|
||||
await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Wrong_Host()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateLowLevelMqttClient();
|
||||
var options = new MqttClientOptionsBuilder().WithTcpServer("123.456.789.10").Build();
|
||||
|
||||
await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Loose_Connection()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreServerLogErrors = true;
|
||||
|
||||
testEnvironment.ServerPort = 8364;
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectLowLevelClient(o => o.WithTimeout(TimeSpan.Zero));
|
||||
|
||||
await Authenticate(client).ConfigureAwait(false);
|
||||
|
||||
await server.StopAsync();
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
try
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var factory = new MqttClientFactory();
|
||||
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
||||
|
||||
await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
|
||||
|
||||
var receivedPacket = await Authenticate(lowLevelClient).ConfigureAwait(false);
|
||||
|
||||
await lowLevelClient.DisconnectAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
Assert.IsNotNull(receivedPacket);
|
||||
Assert.AreEqual(MqttConnectReturnCode.ConnectionAccepted, receivedPacket.ReturnCode);
|
||||
}
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None).ConfigureAwait(false);
|
||||
await Task.Delay(2000);
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
{
|
||||
Assert.IsTrue(exception.InnerException is SocketException);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Assert.Fail("Wrong exception type thrown.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_And_Disconnect()
|
||||
Assert.Fail("This MUST fail");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Maintain_IsConnected_Property()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreServerLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
using var lowLevelClient = testEnvironment.CreateLowLevelClient();
|
||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithTimeout(TimeSpan.FromSeconds(1)).Build();
|
||||
|
||||
await lowLevelClient.ConnectAsync(clientOptions, CancellationToken.None);
|
||||
|
||||
Assert.IsTrue(lowLevelClient.IsConnected);
|
||||
|
||||
await server.StopAsync();
|
||||
server.Dispose();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsTrue(lowLevelClient.IsConnected);
|
||||
|
||||
try
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
await lowLevelClient.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
await LongTestDelay();
|
||||
|
||||
var lowLevelClient = testEnvironment.ClientFactory.CreateLowLevelMqttClient();
|
||||
|
||||
await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
|
||||
|
||||
await lowLevelClient.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
await lowLevelClient.ReceiveAsync(timeout.Token);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// The connection is now closed!
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Not_Existing_Broker()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateLowLevelMqttClient();
|
||||
var options = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
|
||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||
}
|
||||
|
||||
await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Subscribe()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Wrong_Host()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateLowLevelMqttClient();
|
||||
var options = new MqttClientOptionsBuilder().WithTcpServer("123.456.789.10").Build();
|
||||
var factory = new MqttClientFactory();
|
||||
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
||||
|
||||
await client.ConnectAsync(options, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
|
||||
|
||||
[TestMethod]
|
||||
public async Task Loose_Connection()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreServerLogErrors = true;
|
||||
await Authenticate(lowLevelClient).ConfigureAwait(false);
|
||||
|
||||
testEnvironment.ServerPort = 8364;
|
||||
var server = await testEnvironment.StartServer();
|
||||
var receivedPacket = await Subscribe(lowLevelClient, "a").ConfigureAwait(false);
|
||||
|
||||
var client = await testEnvironment.ConnectLowLevelClient(o => o.WithTimeout(TimeSpan.Zero));
|
||||
await lowLevelClient.DisconnectAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
await Authenticate(client).ConfigureAwait(false);
|
||||
Assert.IsNotNull(receivedPacket);
|
||||
Assert.AreEqual(MqttSubscribeReasonCode.GrantedQoS0, receivedPacket.ReasonCodes[0]);
|
||||
}
|
||||
|
||||
await server.StopAsync();
|
||||
|
||||
await Task.Delay(2000);
|
||||
|
||||
try
|
||||
async Task<MqttConnAckPacket> Authenticate(ILowLevelMqttClient client)
|
||||
{
|
||||
await client.SendAsync(
|
||||
new MqttConnectPacket
|
||||
{
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None).ConfigureAwait(false);
|
||||
await Task.Delay(2000);
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
CleanSession = true,
|
||||
ClientId = TestContext.TestName,
|
||||
Username = "user",
|
||||
Password = Encoding.UTF8.GetBytes("pass")
|
||||
},
|
||||
CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false) as MqttConnAckPacket;
|
||||
}
|
||||
|
||||
async Task<MqttSubAckPacket> Subscribe(ILowLevelMqttClient client, string topic)
|
||||
{
|
||||
await client.SendAsync(
|
||||
new MqttSubscribePacket
|
||||
{
|
||||
Assert.IsTrue(exception.InnerException is SocketException);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Assert.Fail("Wrong exception type thrown.");
|
||||
}
|
||||
|
||||
Assert.Fail("This MUST fail");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Maintain_IsConnected_Property()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreServerLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
using (var lowLevelClient = testEnvironment.CreateLowLevelClient())
|
||||
{
|
||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithTimeout(TimeSpan.FromSeconds(1)).Build();
|
||||
|
||||
await lowLevelClient.ConnectAsync(clientOptions, CancellationToken.None);
|
||||
|
||||
Assert.IsTrue(lowLevelClient.IsConnected);
|
||||
|
||||
await server.StopAsync();
|
||||
server.Dispose();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsTrue(lowLevelClient.IsConnected);
|
||||
|
||||
try
|
||||
PacketIdentifier = 1,
|
||||
TopicFilters =
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
new MqttTopicFilter
|
||||
{
|
||||
await lowLevelClient.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
await LongTestDelay();
|
||||
|
||||
await lowLevelClient.ReceiveAsync(timeout.Token);
|
||||
Topic = topic
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
},
|
||||
CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var factory = new MqttClientFactory();
|
||||
var lowLevelClient = factory.CreateLowLevelMqttClient();
|
||||
|
||||
await lowLevelClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build(), CancellationToken.None);
|
||||
|
||||
await Authenticate(lowLevelClient).ConfigureAwait(false);
|
||||
|
||||
var receivedPacket = await Subscribe(lowLevelClient, "a").ConfigureAwait(false);
|
||||
|
||||
await lowLevelClient.DisconnectAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
Assert.IsNotNull(receivedPacket);
|
||||
Assert.AreEqual(MqttSubscribeReasonCode.GrantedQoS0, receivedPacket.ReasonCodes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<MqttConnAckPacket> Authenticate(ILowLevelMqttClient client)
|
||||
{
|
||||
await client.SendAsync(
|
||||
new MqttConnectPacket
|
||||
{
|
||||
CleanSession = true,
|
||||
ClientId = TestContext.TestName,
|
||||
Username = "user",
|
||||
Password = Encoding.UTF8.GetBytes("pass")
|
||||
},
|
||||
CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false) as MqttConnAckPacket;
|
||||
}
|
||||
|
||||
async Task<MqttSubAckPacket> Subscribe(ILowLevelMqttClient client, string topic)
|
||||
{
|
||||
await client.SendAsync(
|
||||
new MqttSubscribePacket
|
||||
{
|
||||
PacketIdentifier = 1,
|
||||
TopicFilters =
|
||||
{
|
||||
new MqttTopicFilter
|
||||
{
|
||||
Topic = topic
|
||||
}
|
||||
}
|
||||
},
|
||||
CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false) as MqttSubAckPacket;
|
||||
}
|
||||
return await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false) as MqttSubAckPacket;
|
||||
}
|
||||
}
|
||||
@@ -14,284 +14,284 @@ using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Server.EnhancedAuthentication;
|
||||
|
||||
namespace MQTTnet.Tests.Clients.MqttClient
|
||||
namespace MQTTnet.Tests.Clients.MqttClient;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttClient_Connection_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttClient_Connection_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Invalid_Server_Port_Not_Opened()
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Invalid_Server_Port_Not_Opened()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateMqttClient();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", 12345).Build(), timeout.Token);
|
||||
}
|
||||
var client = new MqttClientFactory().CreateMqttClient();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", 12345).Build(), timeout.Token);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(OperationCanceledException))]
|
||||
public async Task Connect_To_Invalid_Server_Wrong_IP()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateMqttClient();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2));
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build(), timeout.Token);
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(OperationCanceledException))]
|
||||
public async Task Connect_To_Invalid_Server_Wrong_IP()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateMqttClient();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2));
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build(), timeout.Token);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Invalid_Server_Wrong_Protocol()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateMqttClient();
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("http://127.0.0.1", 12345).WithTimeout(TimeSpan.FromSeconds(2)).Build());
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Invalid_Server_Wrong_Protocol()
|
||||
{
|
||||
var client = new MqttClientFactory().CreateMqttClient();
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("http://127.0.0.1", 12345).WithTimeout(TimeSpan.FromSeconds(2)).Build()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ConnectTimeout_Throws_Exception()
|
||||
[TestMethod]
|
||||
public async Task ConnectTimeout_Throws_Exception()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
using var client = factory.CreateMqttClient();
|
||||
var disconnectHandlerCalled = false;
|
||||
try
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
using var client = factory.CreateMqttClient();
|
||||
var disconnectHandlerCalled = false;
|
||||
try
|
||||
client.DisconnectedAsync += _ =>
|
||||
{
|
||||
client.DisconnectedAsync += _ =>
|
||||
{
|
||||
disconnectHandlerCalled = true;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build());
|
||||
|
||||
Assert.Fail("Must fail!");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Assert.IsNotNull(exception);
|
||||
Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
|
||||
}
|
||||
|
||||
await LongTestDelay(); // disconnected handler is called async
|
||||
Assert.IsTrue(disconnectHandlerCalled);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Clean()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
disconnectHandlerCalled = true;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build());
|
||||
|
||||
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
||||
Assert.Fail("Must fail!");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Assert.IsNotNull(exception);
|
||||
Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Clean_With_Custom_Reason()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
await LongTestDelay(); // disconnected handler is called async
|
||||
Assert.IsTrue(disconnectHandlerCalled);
|
||||
}
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += args =>
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Clean()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Clean_With_Custom_Reason()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Clean_With_User_Properties()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithUserProperty("test_name", "test_value").Build();
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsNotNull(eventArgs.UserProperties);
|
||||
Assert.AreEqual(1, eventArgs.UserProperties.Count);
|
||||
Assert.AreEqual("test_name", eventArgs.UserProperties[0].Name);
|
||||
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
|
||||
}
|
||||
|
||||
class TestClientKerberosAuthenticationHandler : IMqttEnhancedAuthenticationHandler
|
||||
{
|
||||
public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.AuthenticationMethod != "GS2-KRB5")
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
throw new InvalidOperationException("Wrong authentication method");
|
||||
}
|
||||
|
||||
var sendOptions = new SendMqttEnhancedAuthenticationDataOptions
|
||||
{
|
||||
Data = "initial context token"u8.ToArray()
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken);
|
||||
|
||||
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
|
||||
var response = await eventArgs.ReceiveAsync(eventArgs.CancellationToken);
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(response.AuthenticationData), "reply context token");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.AreEqual(MqttDisconnectReasonCode.MessageRateTooHigh, eventArgs.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Clean_With_User_Properties()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += args =>
|
||||
// No further data is required, but we have to fulfil the exchange.
|
||||
sendOptions = new SendMqttEnhancedAuthenticationDataOptions
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
Data = []
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var disconnectOptions = testEnvironment.ClientFactory.CreateClientDisconnectOptionsBuilder().WithUserProperty("test_name", "test_value").Build();
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsNotNull(eventArgs.UserProperties);
|
||||
Assert.AreEqual(1, eventArgs.UserProperties.Count);
|
||||
Assert.AreEqual("test_name", eventArgs.UserProperties[0].Name);
|
||||
Assert.AreEqual("test_value", eventArgs.UserProperties[0].Value);
|
||||
}
|
||||
|
||||
class TestClientKerberosAuthenticationHandler : IMqttEnhancedAuthenticationHandler
|
||||
{
|
||||
public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.AuthenticationMethod != "GS2-KRB5")
|
||||
{
|
||||
throw new InvalidOperationException("Wrong authentication method");
|
||||
}
|
||||
|
||||
var sendOptions = new SendMqttEnhancedAuthenticationDataOptions
|
||||
{
|
||||
Data = "initial context token"u8.ToArray()
|
||||
};
|
||||
|
||||
await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken);
|
||||
|
||||
var response = await eventArgs.ReceiveAsync(eventArgs.CancellationToken);
|
||||
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(response.AuthenticationData), "reply context token");
|
||||
|
||||
// No further data is required, but we have to fulfil the exchange.
|
||||
sendOptions = new SendMqttEnhancedAuthenticationDataOptions
|
||||
{
|
||||
Data = []
|
||||
};
|
||||
|
||||
await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_Enhanced_Authentication()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += async args =>
|
||||
{
|
||||
if (args.AuthenticationMethod == "GS2-KRB5")
|
||||
{
|
||||
var result = await args.ExchangeEnhancedAuthenticationAsync(new ExchangeEnhancedAuthenticationOptions(), args.CancellationToken);
|
||||
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(result.AuthenticationData), "initial context token");
|
||||
|
||||
var authOptions = testEnvironment.ServerFactory.CreateExchangeExtendedAuthenticationOptionsBuilder().WithAuthenticationData("reply context token").Build();
|
||||
|
||||
result = await args.ExchangeEnhancedAuthenticationAsync(authOptions, args.CancellationToken);
|
||||
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(result.AuthenticationData), "");
|
||||
|
||||
args.ResponseAuthenticationData = "outcome of authentication"u8.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
|
||||
}
|
||||
};
|
||||
|
||||
// Use Kerberos sample from the MQTT RFC.
|
||||
var kerberosAuthenticationHandler = new TestClientKerberosAuthenticationHandler();
|
||||
|
||||
var clientOptions = testEnvironment.CreateDefaultClientOptionsBuilder().WithEnhancedAuthentication("GS2-KRB5").WithEnhancedAuthenticationHandler(kerberosAuthenticationHandler);
|
||||
var client = await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task No_Unobserved_Exception()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var options = new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").WithTimeout(TimeSpan.FromSeconds(2)).Build();
|
||||
|
||||
try
|
||||
{
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(0.5));
|
||||
await client.ConnectAsync(options, timeout.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
client.Dispose();
|
||||
|
||||
// These delays and GC calls are required in order to make calling the finalizer reproducible.
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_Non_Success()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += args =>
|
||||
{
|
||||
args.ResponseUserProperties = [new("Property", "Value")];
|
||||
|
||||
args.ReasonCode = MqttConnectReasonCode.QuotaExceeded;
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptionsBuilder().Build());
|
||||
|
||||
Assert.IsNotNull(response);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.QuotaExceeded, response.ResultCode);
|
||||
Assert.AreEqual(response.UserProperties[0].Name, "Property");
|
||||
Assert.AreEqual(response.UserProperties[0].Value, "Value");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Throw_Proper_Exception_When_Not_Connected()
|
||||
{
|
||||
try
|
||||
{
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
await mqttClient.SubscribeAsync("test", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
catch (MqttClientNotConnectedException exception)
|
||||
{
|
||||
if (exception.Message == "The MQTT client is not connected.")
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Fail();
|
||||
await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_Enhanced_Authentication()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += async args =>
|
||||
{
|
||||
if (args.AuthenticationMethod == "GS2-KRB5")
|
||||
{
|
||||
var result = await args.ExchangeEnhancedAuthenticationAsync(new ExchangeEnhancedAuthenticationOptions(), args.CancellationToken);
|
||||
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(result.AuthenticationData), "initial context token");
|
||||
|
||||
var authOptions = testEnvironment.ServerFactory.CreateExchangeExtendedAuthenticationOptionsBuilder().WithAuthenticationData("reply context token").Build();
|
||||
|
||||
result = await args.ExchangeEnhancedAuthenticationAsync(authOptions, args.CancellationToken);
|
||||
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(result.AuthenticationData), "");
|
||||
|
||||
args.ResponseAuthenticationData = "outcome of authentication"u8.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
|
||||
}
|
||||
};
|
||||
|
||||
// Use Kerberos sample from the MQTT RFC.
|
||||
var kerberosAuthenticationHandler = new TestClientKerberosAuthenticationHandler();
|
||||
|
||||
var clientOptions = testEnvironment.CreateDefaultClientOptionsBuilder().WithEnhancedAuthentication("GS2-KRB5").WithEnhancedAuthenticationHandler(kerberosAuthenticationHandler);
|
||||
var client = await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task No_Unobserved_Exception()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var options = new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").WithTimeout(TimeSpan.FromSeconds(2)).Build();
|
||||
|
||||
try
|
||||
{
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(0.5));
|
||||
await client.ConnectAsync(options, timeout.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
client.Dispose();
|
||||
|
||||
// These delays and GC calls are required in order to make calling the finalizer reproducible.
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_Non_Success()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += args =>
|
||||
{
|
||||
args.ResponseUserProperties = [new("Property", "Value")];
|
||||
|
||||
args.ReasonCode = MqttConnectReasonCode.QuotaExceeded;
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptionsBuilder().Build());
|
||||
|
||||
Assert.IsNotNull(response);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.QuotaExceeded, response.ResultCode);
|
||||
Assert.AreEqual(response.UserProperties[0].Name, "Property");
|
||||
Assert.AreEqual(response.UserProperties[0].Value, "Value");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Throw_Proper_Exception_When_Not_Connected()
|
||||
{
|
||||
try
|
||||
{
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
await mqttClient.SubscribeAsync("test", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
catch (MqttClientNotConnectedException exception)
|
||||
{
|
||||
if (exception.Message == "The MQTT client is not connected.")
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Fail();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,23 +3,22 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace MQTTnet.Tests.Clients
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttClientOptionsBuilder_Tests
|
||||
{
|
||||
[TestMethod]
|
||||
public void WithConnectionUri_Credential_Test()
|
||||
{
|
||||
var options = new MqttClientOptionsBuilder()
|
||||
.WithConnectionUri("mqtt://user:password@127.0.0.1")
|
||||
.Build();
|
||||
namespace MQTTnet.Tests.Clients;
|
||||
|
||||
Assert.AreEqual("user", options.Credentials.GetUserName(null));
|
||||
Assert.IsTrue(Encoding.UTF8.GetBytes("password").SequenceEqual(options.Credentials.GetPassword(null)));
|
||||
}
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class MqttClientOptionsBuilder_Tests
|
||||
{
|
||||
[TestMethod]
|
||||
public void WithConnectionUri_Credential_Test()
|
||||
{
|
||||
var options = new MqttClientOptionsBuilder()
|
||||
.WithConnectionUri("mqtt://user:password@127.0.0.1")
|
||||
.Build();
|
||||
|
||||
Assert.AreEqual("user", options.Credentials.GetUserName(null));
|
||||
Assert.IsTrue("password"u8.ToArray().SequenceEqual(options.Credentials.GetPassword(null)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,61 +6,61 @@ using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Tests.Diagnostics
|
||||
namespace MQTTnet.Tests.Diagnostics;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Logger_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Logger_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public void Log_Without_Source()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Log_Without_Source()
|
||||
var logger = new MqttNetEventLogger();
|
||||
|
||||
MqttNetLogMessage logMessage = null;
|
||||
logger.LogMessagePublished += (_, e) => { logMessage = e.LogMessage; };
|
||||
|
||||
logger.Publish(MqttNetLogLevel.Info, "SOURCE", "MESSAGE", ["ABC"], new InvalidOperationException());
|
||||
|
||||
Assert.AreEqual(MqttNetLogLevel.Info, logMessage.Level);
|
||||
Assert.AreEqual("SOURCE", logMessage.Source);
|
||||
Assert.AreEqual("MESSAGE", logMessage.Message);
|
||||
Assert.AreEqual("InvalidOperationException", logMessage.Exception.GetType().Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Root_Log_Messages()
|
||||
{
|
||||
var logger = new MqttNetEventLogger();
|
||||
var childLogger = logger.WithSource("Source1");
|
||||
|
||||
var logMessagesCount = 0;
|
||||
|
||||
logger.LogMessagePublished += (_, _) => { logMessagesCount++; };
|
||||
|
||||
childLogger.Verbose("Verbose");
|
||||
childLogger.Info("Info");
|
||||
childLogger.Warning((Exception)null, "Warning");
|
||||
childLogger.Error(null, "Error");
|
||||
|
||||
Assert.AreEqual(4, logMessagesCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Use_Custom_Log_Id()
|
||||
{
|
||||
var logger = new MqttNetEventLogger("logId");
|
||||
var childLogger = logger.WithSource("Source1");
|
||||
|
||||
logger.LogMessagePublished += (_, e) =>
|
||||
{
|
||||
var logger = new MqttNetEventLogger();
|
||||
Assert.AreEqual("logId", e.LogMessage.LogId);
|
||||
Assert.AreEqual("Source1", e.LogMessage.Source);
|
||||
};
|
||||
|
||||
MqttNetLogMessage logMessage = null;
|
||||
logger.LogMessagePublished += (s, e) => { logMessage = e.LogMessage; };
|
||||
|
||||
logger.Publish(MqttNetLogLevel.Info, "SOURCE", "MESSAGE", new object[] { "ABC" }, new InvalidOperationException());
|
||||
|
||||
Assert.AreEqual(MqttNetLogLevel.Info, logMessage.Level);
|
||||
Assert.AreEqual("SOURCE", logMessage.Source);
|
||||
Assert.AreEqual("MESSAGE", logMessage.Message);
|
||||
Assert.AreEqual("InvalidOperationException", logMessage.Exception.GetType().Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Root_Log_Messages()
|
||||
{
|
||||
var logger = new MqttNetEventLogger();
|
||||
var childLogger = logger.WithSource("Source1");
|
||||
|
||||
var logMessagesCount = 0;
|
||||
|
||||
logger.LogMessagePublished += (s, e) => { logMessagesCount++; };
|
||||
|
||||
childLogger.Verbose("Verbose");
|
||||
childLogger.Info("Info");
|
||||
childLogger.Warning((Exception)null, "Warning");
|
||||
childLogger.Error(null, "Error");
|
||||
|
||||
Assert.AreEqual(4, logMessagesCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Use_Custom_Log_Id()
|
||||
{
|
||||
var logger = new MqttNetEventLogger("logId");
|
||||
var childLogger = logger.WithSource("Source1");
|
||||
|
||||
logger.LogMessagePublished += (s, e) =>
|
||||
{
|
||||
Assert.AreEqual("logId", e.LogMessage.LogId);
|
||||
Assert.AreEqual("Source1", e.LogMessage.Source);
|
||||
};
|
||||
|
||||
childLogger.Verbose("Verbose");
|
||||
childLogger.Info("Info");
|
||||
childLogger.Warning((Exception)null, "Warning");
|
||||
childLogger.Error(null, "Error");
|
||||
}
|
||||
childLogger.Verbose("Verbose");
|
||||
childLogger.Info("Info");
|
||||
childLogger.Warning((Exception)null, "Warning");
|
||||
childLogger.Error(null, "Error");
|
||||
}
|
||||
}
|
||||
@@ -10,41 +10,37 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Diagnostics
|
||||
namespace MQTTnet.Tests.Diagnostics;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class PacketInspection_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class PacketInspection_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Inspect_Client_Packets()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Inspect_Client_Packets()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
using var mqttClient = testEnvironment.CreateClient();
|
||||
var mqttClientOptions = testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithClientId("CLIENT_ID") // Must be fixed.
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build();
|
||||
|
||||
var packets = new List<string>();
|
||||
|
||||
mqttClient.InspectPacketAsync += eventArgs =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
using (var mqttClient = testEnvironment.CreateClient())
|
||||
{
|
||||
var mqttClientOptions = testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithClientId("CLIENT_ID") // Must be fixed.
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build();
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var packets = new List<string>();
|
||||
|
||||
mqttClient.InspectPacketAsync += eventArgs =>
|
||||
{
|
||||
packets.Add(eventArgs.Direction + ":" + Convert.ToBase64String(eventArgs.Buffer));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Assert.AreEqual(2, packets.Count);
|
||||
Assert.AreEqual("Outbound:ECwABE1RVFQEAgAPACBJbnNwZWN0X0NsaWVudF9QYWNrZXRzX0NMSUVOVF9JRA==", packets[0]); // CONNECT
|
||||
Assert.AreEqual("Inbound:IAIAAA==", packets[1]); // CONNACK
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(2, packets.Count);
|
||||
Assert.AreEqual("Outbound:ECwABE1RVFQEAgAPACBJbnNwZWN0X0NsaWVudF9QYWNrZXRzX0NMSUVOVF9JRA==", packets[0]); // CONNECT
|
||||
Assert.AreEqual("Inbound:IAIAAA==", packets[1]); // CONNACK
|
||||
}
|
||||
}
|
||||
@@ -3,29 +3,28 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Tests.Diagnostics
|
||||
namespace MQTTnet.Tests.Diagnostics;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class SourceLogger_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class SourceLogger_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public void Log_With_Source()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Log_With_Source()
|
||||
MqttNetLogMessage logMessage = null;
|
||||
|
||||
var logger = new MqttNetEventLogger();
|
||||
logger.LogMessagePublished += (_, e) =>
|
||||
{
|
||||
MqttNetLogMessage logMessage = null;
|
||||
logMessage = e.LogMessage;
|
||||
};
|
||||
|
||||
var logger = new MqttNetEventLogger();
|
||||
logger.LogMessagePublished += (s, e) =>
|
||||
{
|
||||
logMessage = e.LogMessage;
|
||||
};
|
||||
var sourceLogger = logger.WithSource("The_Source");
|
||||
sourceLogger.Info("MESSAGE", (object)null, (object)null);
|
||||
|
||||
var sourceLogger = logger.WithSource("The_Source");
|
||||
sourceLogger.Info("MESSAGE", (object)null, (object)null);
|
||||
|
||||
Assert.AreEqual("The_Source", logMessage.Source);
|
||||
}
|
||||
Assert.AreEqual("The_Source", logMessage.Source);
|
||||
}
|
||||
}
|
||||
@@ -8,266 +8,254 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Extensions.TopicTemplate;
|
||||
|
||||
namespace MQTTnet.Tests.Extensions
|
||||
namespace MQTTnet.Tests.Extensions;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttTopicTemplate_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttTopicTemplate_Tests
|
||||
[TestMethod]
|
||||
public void AcceptsEmptyValue()
|
||||
{
|
||||
[TestMethod]
|
||||
public void BasicOps()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{param}/D");
|
||||
Assert.AreEqual("A/B/C/D", template.WithParameter("param", "C").Template);
|
||||
Assert.AreEqual("A/B/+/D", template.WithoutParameter("param").Template);
|
||||
Assert.AreEqual("A/B/+/D", template.TopicFilter);
|
||||
Assert.AreEqual("A/B/+/D/#", template.TopicTreeRootFilter);
|
||||
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
||||
template.WithParameter("foo", "");
|
||||
}
|
||||
|
||||
// corner cases
|
||||
Assert.AreEqual("#", new MqttTopicTemplate("#").TopicFilter);
|
||||
Assert.AreEqual("+", new MqttTopicTemplate("+").TopicFilter);
|
||||
Assert.AreEqual("/", new MqttTopicTemplate("/").TopicFilter);
|
||||
}
|
||||
[TestMethod]
|
||||
public void AcceptsValidTopics()
|
||||
{
|
||||
_ = new MqttTopicTemplate("A");
|
||||
_ = new MqttTopicTemplate("/");
|
||||
_ = new MqttTopicTemplate("/A");
|
||||
_ = new MqttTopicTemplate("A/#");
|
||||
_ = new MqttTopicTemplate("$SYS/#");
|
||||
_ = new MqttTopicTemplate("////");
|
||||
_ = new MqttTopicTemplate("A/B/{foo}/D/");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BasicOpsWithHash()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{param}/D/#");
|
||||
Assert.AreEqual("A/B/C/D/#", template.WithParameter("param", "C").Template);
|
||||
Assert.AreEqual("A/B/+/D/#", template.WithoutParameter("param").Template);
|
||||
Assert.AreEqual("A/B/+/D/#", template.TopicFilter);
|
||||
Assert.AreEqual("A/B/+/D/#", template.TopicTreeRootFilter);
|
||||
}
|
||||
[TestMethod]
|
||||
public void BasicOps()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{param}/D");
|
||||
Assert.AreEqual("A/B/C/D", template.WithParameter("param", "C").Template);
|
||||
Assert.AreEqual("A/B/+/D", template.WithoutParameter("param").Template);
|
||||
Assert.AreEqual("A/B/+/D", template.TopicFilter);
|
||||
Assert.AreEqual("A/B/+/D/#", template.TopicTreeRootFilter);
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void RejectsReservedChars1()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
||||
template.WithParameter("foo", "a#");
|
||||
}
|
||||
// corner cases
|
||||
Assert.AreEqual("#", new MqttTopicTemplate("#").TopicFilter);
|
||||
Assert.AreEqual("+", new MqttTopicTemplate("+").TopicFilter);
|
||||
Assert.AreEqual("/", new MqttTopicTemplate("/").TopicFilter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void RejectsReservedChars2()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
||||
template.WithParameter("foo", "a+b");
|
||||
}
|
||||
[TestMethod]
|
||||
public void BasicOpsWithHash()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{param}/D/#");
|
||||
Assert.AreEqual("A/B/C/D/#", template.WithParameter("param", "C").Template);
|
||||
Assert.AreEqual("A/B/+/D/#", template.WithoutParameter("param").Template);
|
||||
Assert.AreEqual("A/B/+/D/#", template.TopicFilter);
|
||||
Assert.AreEqual("A/B/+/D/#", template.TopicTreeRootFilter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void RejectsReservedChars3()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
||||
template.WithParameter("foo", "a/b");
|
||||
}
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilter()
|
||||
{
|
||||
var template1 = new MqttTopicTemplate("A/v1/{param}/F");
|
||||
var template2 = new MqttTopicTemplate("A/v1/E/F");
|
||||
var template3 = new MqttTopicTemplate("A/v1/E/F/G/H");
|
||||
var canonicalFilter = MqttTopicTemplate.FindCanonicalPrefix([template1, template2, template3]);
|
||||
// possible improvement: Assert.AreEqual("A/v1/+/F", canonicalFilter.TopicFilter);
|
||||
Assert.AreEqual("A/v1/+", canonicalFilter.TopicFilter);
|
||||
Assert.AreEqual("A/v1/+/#", canonicalFilter.TopicTreeRootFilter);
|
||||
|
||||
[TestMethod]
|
||||
public void AcceptsEmptyValue()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
||||
template.WithParameter("foo", "");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void RejectsEmptyTemplate()
|
||||
{
|
||||
var _ = new MqttTopicTemplate("");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void RejectsNullTemplate()
|
||||
{
|
||||
var _ = new MqttTopicTemplate(null);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IgnoresEmptyParameters()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{}/D");
|
||||
Assert.IsFalse(template.Parameters.Any());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AcceptsValidTopics()
|
||||
{
|
||||
_ = new MqttTopicTemplate("A");
|
||||
_ = new MqttTopicTemplate("/");
|
||||
_ = new MqttTopicTemplate("/A");
|
||||
_ = new MqttTopicTemplate("A/#");
|
||||
_ = new MqttTopicTemplate("$SYS/#");
|
||||
_ = new MqttTopicTemplate("////");
|
||||
_ = new MqttTopicTemplate("A/B/{foo}/D/");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DynamicRoutingSupport()
|
||||
{
|
||||
var v1template = new MqttTopicTemplate("A/v1/{id}/F");
|
||||
var v2template = new MqttTopicTemplate("A/v2/foo/{id}/{bar}");
|
||||
var incoming_v1_topic = "A/v1/32/F";
|
||||
|
||||
var compat_v2_topic = v2template
|
||||
.WithParameterValuesFrom(
|
||||
v1template.ParseParameterValues(incoming_v1_topic))
|
||||
.WithParameter("bar", "unknown");
|
||||
|
||||
// id: 32 was relocated and bar: unknown added
|
||||
Assert.AreEqual("A/v2/foo/32/unknown", compat_v2_topic.Template);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SubscriptionSupport()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/v1/{param}/F");
|
||||
var filter = template.BuildFilter()
|
||||
.WithAtLeastOnceQoS()
|
||||
.WithNoLocal()
|
||||
.Build();
|
||||
|
||||
Assert.AreEqual("A/v1/+/F", filter.Topic);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SubscriptionSupport2()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/v1/{param}/F");
|
||||
|
||||
var subscribeOptions = new MqttClientFactory().CreateSubscribeOptionsBuilder()
|
||||
.WithTopicTemplate(template)
|
||||
.WithSubscriptionIdentifier(5)
|
||||
.Build();
|
||||
|
||||
Assert.AreEqual("A/v1/+/F", subscribeOptions.TopicFilters[0].Topic);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SendAndSubscribeSupport()
|
||||
{
|
||||
var template = new MqttTopicTemplate("App/v1/{sender}/message");
|
||||
|
||||
var filter = new MqttTopicFilterBuilder()
|
||||
.WithTopicTemplate(template)
|
||||
.WithAtLeastOnceQoS()
|
||||
.WithNoLocal()
|
||||
.Build();
|
||||
|
||||
Assert.AreEqual("App/v1/+/message", filter.Topic);
|
||||
|
||||
var myTopic = template.WithParameter("sender", "me, myself & i");
|
||||
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithTopicTemplate(
|
||||
myTopic)
|
||||
.WithPayload("Hello!")
|
||||
.Build();
|
||||
|
||||
Assert.IsTrue(message.MatchesTopicTemplate(template));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SendAndSubscribeSupport2()
|
||||
{
|
||||
var template = new MqttTopicTemplate("App/v1/{sender}/message");
|
||||
Assert.ThrowsException<ArgumentException>(() =>
|
||||
template.BuildMessage());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilter()
|
||||
{
|
||||
var template1 = new MqttTopicTemplate("A/v1/{param}/F");
|
||||
var template2 = new MqttTopicTemplate("A/v1/E/F");
|
||||
var template3 = new MqttTopicTemplate("A/v1/E/F/G/H");
|
||||
var canonicalFilter = MqttTopicTemplate.FindCanonicalPrefix(new[] { template1, template2, template3 });
|
||||
// possible improvement: Assert.AreEqual("A/v1/+/F", canonicalFilter.TopicFilter);
|
||||
Assert.AreEqual("A/v1/+", canonicalFilter.TopicFilter);
|
||||
Assert.AreEqual("A/v1/+/#", canonicalFilter.TopicTreeRootFilter);
|
||||
|
||||
var template2b = new MqttTopicTemplate("A/v1/E/X");
|
||||
canonicalFilter = MqttTopicTemplate.FindCanonicalPrefix(new[] { template1, template2, template3, template2b });
|
||||
Assert.AreEqual("A/v1/+", canonicalFilter.Template);
|
||||
Assert.AreEqual("A/v1/+", canonicalFilter.TopicFilter);
|
||||
Assert.AreEqual("A/v1/+/#", canonicalFilter.TopicTreeRootFilter);
|
||||
var template2B = new MqttTopicTemplate("A/v1/E/X");
|
||||
canonicalFilter = MqttTopicTemplate.FindCanonicalPrefix([template1, template2, template3, template2B]);
|
||||
Assert.AreEqual("A/v1/+", canonicalFilter.Template);
|
||||
Assert.AreEqual("A/v1/+", canonicalFilter.TopicFilter);
|
||||
Assert.AreEqual("A/v1/+/#", canonicalFilter.TopicTreeRootFilter);
|
||||
|
||||
|
||||
var template4 = new MqttTopicTemplate("A/v2/{prefix}");
|
||||
var canonicalFilter2 = MqttTopicTemplate.FindCanonicalPrefix(new[] { template1, template2, template3, template4 });
|
||||
Assert.AreEqual("A/+", canonicalFilter2.TopicFilter);
|
||||
Assert.AreEqual("A/+/#", canonicalFilter2.TopicTreeRootFilter);
|
||||
}
|
||||
var template4 = new MqttTopicTemplate("A/v2/{prefix}");
|
||||
var canonicalFilter2 = MqttTopicTemplate.FindCanonicalPrefix([template1, template2, template3, template4]);
|
||||
Assert.AreEqual("A/+", canonicalFilter2.TopicFilter);
|
||||
Assert.AreEqual("A/+/#", canonicalFilter2.TopicTreeRootFilter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple1()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(new[]
|
||||
{
|
||||
new MqttTopicTemplate("A"),
|
||||
new MqttTopicTemplate("B")
|
||||
});
|
||||
Assert.AreEqual("#", template.TopicFilter);
|
||||
Assert.AreEqual("#", template.Template);
|
||||
}
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple1()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(
|
||||
[
|
||||
new MqttTopicTemplate("A"),
|
||||
new MqttTopicTemplate("B")
|
||||
]);
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple2()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(new[]
|
||||
{
|
||||
new MqttTopicTemplate("A/v1/{param}/F/"),
|
||||
new MqttTopicTemplate("A/v1/E/X")
|
||||
});
|
||||
Assert.AreEqual("A/v1/+", template.TopicFilter);
|
||||
}
|
||||
Assert.AreEqual("#", template.TopicFilter);
|
||||
Assert.AreEqual("#", template.Template);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple3()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(new[]
|
||||
{
|
||||
new MqttTopicTemplate("A/v1/{param}/F"),
|
||||
new MqttTopicTemplate("A/v1/{param}/F/X")
|
||||
});
|
||||
Assert.AreEqual("A/v1/+/+", template.TopicFilter);
|
||||
Assert.AreEqual("A/v1/{param}/+", template.Template);
|
||||
}
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple2()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(
|
||||
[
|
||||
new MqttTopicTemplate("A/v1/{param}/F/"),
|
||||
new MqttTopicTemplate("A/v1/E/X")
|
||||
]);
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple4()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(new[]
|
||||
{
|
||||
new MqttTopicTemplate("A/v1/{param}/FFA"),
|
||||
new MqttTopicTemplate("A/v1/{param}/FX")
|
||||
});
|
||||
Assert.AreEqual("A/v1/+/+", template.TopicFilter);
|
||||
Assert.AreEqual("A/v1/{param}/+", template.Template);
|
||||
}
|
||||
Assert.AreEqual("A/v1/+", template.TopicFilter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple5()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(new[]
|
||||
{
|
||||
new MqttTopicTemplate("A/v1/E/F/G/H"),
|
||||
new MqttTopicTemplate("A/v2/{param}")
|
||||
});
|
||||
Assert.AreEqual("A/+", template.TopicFilter);
|
||||
Assert.AreEqual("A/+/#", template.TopicTreeRootFilter);
|
||||
}
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple3()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(
|
||||
[
|
||||
new MqttTopicTemplate("A/v1/{param}/F"),
|
||||
new MqttTopicTemplate("A/v1/{param}/F/X")
|
||||
]);
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple6()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(new[]
|
||||
{
|
||||
new MqttTopicTemplate("A/{version}/E/F/G/H"),
|
||||
new MqttTopicTemplate("A/{version}/{param}")
|
||||
});
|
||||
Assert.AreEqual("A/+/+", template.TopicFilter);
|
||||
Assert.AreEqual("A/+/+/#", template.TopicTreeRootFilter);
|
||||
}
|
||||
Assert.AreEqual("A/v1/+/+", template.TopicFilter);
|
||||
Assert.AreEqual("A/v1/{param}/+", template.Template);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple4()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(
|
||||
[
|
||||
new MqttTopicTemplate("A/v1/{param}/FFA"),
|
||||
new MqttTopicTemplate("A/v1/{param}/FX")
|
||||
]);
|
||||
|
||||
Assert.AreEqual("A/v1/+/+", template.TopicFilter);
|
||||
Assert.AreEqual("A/v1/{param}/+", template.Template);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple5()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(
|
||||
[
|
||||
new MqttTopicTemplate("A/v1/E/F/G/H"),
|
||||
new MqttTopicTemplate("A/v2/{param}")
|
||||
]);
|
||||
|
||||
Assert.AreEqual("A/+", template.TopicFilter);
|
||||
Assert.AreEqual("A/+/#", template.TopicTreeRootFilter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanonicalPrefixFilterSimple6()
|
||||
{
|
||||
var template = MqttTopicTemplate.FindCanonicalPrefix(
|
||||
[
|
||||
new MqttTopicTemplate("A/{version}/E/F/G/H"),
|
||||
new MqttTopicTemplate("A/{version}/{param}")
|
||||
]);
|
||||
|
||||
Assert.AreEqual("A/+/+", template.TopicFilter);
|
||||
Assert.AreEqual("A/+/+/#", template.TopicTreeRootFilter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DynamicRoutingSupport()
|
||||
{
|
||||
var v1Template = new MqttTopicTemplate("A/v1/{id}/F");
|
||||
var v2Template = new MqttTopicTemplate("A/v2/foo/{id}/{bar}");
|
||||
var incomingV1Topic = "A/v1/32/F";
|
||||
|
||||
var compatV2Topic = v2Template.WithParameterValuesFrom(v1Template.ParseParameterValues(incomingV1Topic)).WithParameter("bar", "unknown");
|
||||
|
||||
// id: 32 was relocated and bar: unknown added
|
||||
Assert.AreEqual("A/v2/foo/32/unknown", compatV2Topic.Template);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IgnoresEmptyParameters()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{}/D");
|
||||
Assert.IsFalse(template.Parameters.Any());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void RejectsEmptyTemplate()
|
||||
{
|
||||
_ = new MqttTopicTemplate("");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void RejectsNullTemplate()
|
||||
{
|
||||
_ = new MqttTopicTemplate(null);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void RejectsReservedChars1()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
||||
template.WithParameter("foo", "a#");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void RejectsReservedChars2()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
||||
template.WithParameter("foo", "a+b");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void RejectsReservedChars3()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/B/{foo}/D");
|
||||
template.WithParameter("foo", "a/b");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SendAndSubscribeSupport()
|
||||
{
|
||||
var template = new MqttTopicTemplate("App/v1/{sender}/message");
|
||||
|
||||
var filter = new MqttTopicFilterBuilder().WithTopicTemplate(template).WithAtLeastOnceQoS().WithNoLocal().Build();
|
||||
|
||||
Assert.AreEqual("App/v1/+/message", filter.Topic);
|
||||
|
||||
var myTopic = template.WithParameter("sender", "me, myself & i");
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopicTemplate(myTopic).WithPayload("Hello!").Build();
|
||||
|
||||
Assert.IsTrue(message.MatchesTopicTemplate(template));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SendAndSubscribeSupport2()
|
||||
{
|
||||
var template = new MqttTopicTemplate("App/v1/{sender}/message");
|
||||
Assert.ThrowsException<ArgumentException>(() => template.BuildMessage());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SubscriptionSupport()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/v1/{param}/F");
|
||||
var filter = template.BuildFilter().WithAtLeastOnceQoS().WithNoLocal().Build();
|
||||
|
||||
Assert.AreEqual("A/v1/+/F", filter.Topic);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SubscriptionSupport2()
|
||||
{
|
||||
var template = new MqttTopicTemplate("A/v1/{param}/F");
|
||||
|
||||
var subscribeOptions = new MqttClientFactory().CreateSubscribeOptionsBuilder().WithTopicTemplate(template).WithSubscriptionIdentifier(5).Build();
|
||||
|
||||
Assert.AreEqual("A/v1/+/F", subscribeOptions.TopicFilters[0].Topic);
|
||||
}
|
||||
}
|
||||
@@ -14,268 +14,242 @@ using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Extensions
|
||||
namespace MQTTnet.Tests.Extensions;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Rpc_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Rpc_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Execute_Success_MQTT_V5_Mixed_Clients()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Execute_Success_MQTT_V5_Mixed_Clients()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
var responseSender = await testEnvironment.ConnectClient();
|
||||
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping");
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += async e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var responseSender = await testEnvironment.ConnectClient();
|
||||
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping");
|
||||
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
||||
await responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong").ConfigureAwait(false);
|
||||
};
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += async e =>
|
||||
{
|
||||
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
||||
await responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong");
|
||||
};
|
||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
using var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build());
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
|
||||
using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
|
||||
{
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Execute_Success_Parameters_Propagated_Correctly()
|
||||
[TestMethod]
|
||||
public async Task Execute_Success_Parameters_Propagated_Correctly()
|
||||
{
|
||||
var paramValue = "123";
|
||||
var parameters = new Dictionary<string, object>
|
||||
{
|
||||
var paramValue = "123";
|
||||
var parameters = new Dictionary<string, object>
|
||||
{ TestParametersTopicGenerationStrategy.ExpectedParamName, "123" }
|
||||
};
|
||||
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var responseSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
||||
await responseSender.SubscribeAsync($"MQTTnet.RPC/+/ping/{paramValue}");
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += e => responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong");
|
||||
|
||||
using var rpcClient = await testEnvironment.ConnectRpcClient(new MqttRpcClientOptionsBuilder()
|
||||
.WithTopicGenerationStrategy(new TestParametersTopicGenerationStrategy()).Build());
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", MqttQualityOfServiceLevel.AtMostOnce, parameters);
|
||||
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_0()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.AtMostOnce, MqttProtocolVersion.V311);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_0_MQTT_V5()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.AtMostOnce, MqttProtocolVersion.V500);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_0_MQTT_V5_Use_ResponseTopic()
|
||||
{
|
||||
return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_1()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.AtLeastOnce, MqttProtocolVersion.V311);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_1_MQTT_V5()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.AtLeastOnce, MqttProtocolVersion.V500);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_1_MQTT_V5_Use_ResponseTopic()
|
||||
{
|
||||
return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_2()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.ExactlyOnce, MqttProtocolVersion.V311);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_2_MQTT_V5()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.ExactlyOnce, MqttProtocolVersion.V500);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_2_MQTT_V5_Use_ResponseTopic()
|
||||
{
|
||||
return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||
public async Task Execute_Timeout()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient();
|
||||
|
||||
var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build());
|
||||
await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||
public async Task Execute_Timeout_MQTT_V5_Mixed_Clients()
|
||||
{
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
await testEnvironment.StartServer();
|
||||
var responseSender = await testEnvironment.ConnectClient();
|
||||
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping");
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
using var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build());
|
||||
await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||
public async Task Execute_With_Custom_Topic_Names()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var rpcClient = await testEnvironment.ConnectRpcClient(new MqttRpcClientOptionsBuilder().WithTopicGenerationStrategy(new TestTopicStrategy()).Build());
|
||||
|
||||
await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Use_Factory()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
using var client = factory.CreateMqttClient();
|
||||
var rpcClient = factory.CreateMqttRpcClient(client);
|
||||
|
||||
Assert.IsNotNull(rpcClient);
|
||||
}
|
||||
|
||||
async Task Execute_Success(MqttQualityOfServiceLevel qosLevel, MqttProtocolVersion protocolVersion)
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
var responseSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(protocolVersion));
|
||||
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping", qosLevel);
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += e => responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong");
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient();
|
||||
|
||||
using var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build());
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", qosLevel);
|
||||
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
|
||||
async Task Execute_Success_MQTT_V5(MqttQualityOfServiceLevel qosLevel)
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
var responseSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping", qosLevel);
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += async e =>
|
||||
{
|
||||
await responseSender.PublishStringAsync(e.ApplicationMessage.ResponseTopic, "pong").ConfigureAwait(false);
|
||||
};
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
using var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build());
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", qosLevel);
|
||||
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
|
||||
class TestTopicStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||
{
|
||||
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
||||
{
|
||||
return new MqttRpcTopicPair
|
||||
{
|
||||
{ TestParametersTopicGenerationStrategy.ExpectedParamName, "123" }
|
||||
RequestTopic = "a",
|
||||
ResponseTopic = "b"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
class TestParametersTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||
{
|
||||
internal const string ExpectedParamName = "test_param_name";
|
||||
|
||||
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
||||
{
|
||||
if (context.Parameters == null)
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var responseSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder());
|
||||
await responseSender.SubscribeAsync($"MQTTnet.RPC/+/ping/{paramValue}");
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += e => responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong");
|
||||
|
||||
using (var rpcClient = await testEnvironment.ConnectRpcClient(new MqttRpcClientOptionsBuilder()
|
||||
.WithTopicGenerationStrategy(new TestParametersTopicGenerationStrategy()).Build()))
|
||||
{
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", MqttQualityOfServiceLevel.AtMostOnce, parameters);
|
||||
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
throw new InvalidOperationException("Parameters dictionary expected to be not null");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_0()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.AtMostOnce, MqttProtocolVersion.V311);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_0_MQTT_V5()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.AtMostOnce, MqttProtocolVersion.V500);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_0_MQTT_V5_Use_ResponseTopic()
|
||||
{
|
||||
return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_1()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.AtLeastOnce, MqttProtocolVersion.V311);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_1_MQTT_V5()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.AtLeastOnce, MqttProtocolVersion.V500);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_1_MQTT_V5_Use_ResponseTopic()
|
||||
{
|
||||
return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_2()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.ExactlyOnce, MqttProtocolVersion.V311);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_2_MQTT_V5()
|
||||
{
|
||||
return Execute_Success(MqttQualityOfServiceLevel.ExactlyOnce, MqttProtocolVersion.V500);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Execute_Success_With_QoS_2_MQTT_V5_Use_ResponseTopic()
|
||||
{
|
||||
return Execute_Success_MQTT_V5(MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||
public async Task Execute_Timeout()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
if (!context.Parameters.TryGetValue(ExpectedParamName, out var paramValue))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient();
|
||||
|
||||
var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build());
|
||||
await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
throw new InvalidOperationException($"Parameter with name {ExpectedParamName} not present");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||
public async Task Execute_Timeout_MQTT_V5_Mixed_Clients()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{context.MethodName}/{paramValue}";
|
||||
var responseTopic = requestTopic + "/response";
|
||||
|
||||
return new MqttRpcTopicPair
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var responseSender = await testEnvironment.ConnectClient();
|
||||
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping");
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += async e =>
|
||||
{
|
||||
Assert.IsNull(e.ApplicationMessage.ResponseTopic);
|
||||
await CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
|
||||
{
|
||||
await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationTimedOutException))]
|
||||
public async Task Execute_With_Custom_Topic_Names()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var rpcClient = await testEnvironment.ConnectRpcClient(new MqttRpcClientOptionsBuilder().WithTopicGenerationStrategy(new TestTopicStrategy()).Build());
|
||||
|
||||
await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Use_Factory()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
using (var client = factory.CreateMqttClient())
|
||||
{
|
||||
var rpcClient = factory.CreateMqttRpcClient(client);
|
||||
|
||||
Assert.IsNotNull(rpcClient);
|
||||
}
|
||||
}
|
||||
|
||||
async Task Execute_Success(MqttQualityOfServiceLevel qosLevel, MqttProtocolVersion protocolVersion)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var responseSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(protocolVersion));
|
||||
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping", qosLevel);
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += e => responseSender.PublishStringAsync(e.ApplicationMessage.Topic + "/response", "pong");
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient();
|
||||
|
||||
using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
|
||||
{
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", qosLevel);
|
||||
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task Execute_Success_MQTT_V5(MqttQualityOfServiceLevel qosLevel)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var responseSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
await responseSender.SubscribeAsync("MQTTnet.RPC/+/ping", qosLevel);
|
||||
|
||||
responseSender.ApplicationMessageReceivedAsync += async e =>
|
||||
{
|
||||
await responseSender.PublishStringAsync(e.ApplicationMessage.ResponseTopic, "pong");
|
||||
};
|
||||
|
||||
var requestSender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
using (var rpcClient = new MqttRpcClient(requestSender, new MqttRpcClientOptionsBuilder().Build()))
|
||||
{
|
||||
var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), "ping", "", qosLevel);
|
||||
|
||||
Assert.AreEqual("pong", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestTopicStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||
{
|
||||
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
||||
{
|
||||
return new MqttRpcTopicPair
|
||||
{
|
||||
RequestTopic = "a",
|
||||
ResponseTopic = "b"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class TestParametersTopicGenerationStrategy : IMqttRpcClientTopicGenerationStrategy
|
||||
{
|
||||
internal const string ExpectedParamName = "test_param_name";
|
||||
|
||||
public MqttRpcTopicPair CreateRpcTopics(TopicGenerationContext context)
|
||||
{
|
||||
if (context.Parameters == null)
|
||||
{
|
||||
throw new InvalidOperationException("Parameters dictionary expected to be not null");
|
||||
}
|
||||
|
||||
if (!context.Parameters.TryGetValue(ExpectedParamName, out var paramValue))
|
||||
{
|
||||
throw new InvalidOperationException($"Parameter with name {ExpectedParamName} not present");
|
||||
}
|
||||
|
||||
var requestTopic = $"MQTTnet.RPC/{Guid.NewGuid():N}/{context.MethodName}/{paramValue}";
|
||||
var responseTopic = requestTopic + "/response";
|
||||
|
||||
return new MqttRpcTopicPair
|
||||
{
|
||||
RequestTopic = requestTopic,
|
||||
ResponseTopic = responseTopic
|
||||
};
|
||||
}
|
||||
RequestTopic = requestTopic,
|
||||
ResponseTopic = responseTopic
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,91 +5,90 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Factory
|
||||
namespace MQTTnet.Tests.Factory;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttFactory_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttFactory_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public void Create_ApplicationMessageBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateApplicationMessageBuilder();
|
||||
|
||||
[TestMethod]
|
||||
public void Create_ApplicationMessageBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateApplicationMessageBuilder();
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Create_ClientOptionsBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateClientOptionsBuilder();
|
||||
|
||||
[TestMethod]
|
||||
public void Create_ClientOptionsBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateClientOptionsBuilder();
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Create_ServerOptionsBuilder()
|
||||
{
|
||||
var factory = new MqttServerFactory();
|
||||
var builder = factory.CreateServerOptionsBuilder();
|
||||
|
||||
[TestMethod]
|
||||
public void Create_ServerOptionsBuilder()
|
||||
{
|
||||
var factory = new MqttServerFactory();
|
||||
var builder = factory.CreateServerOptionsBuilder();
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Create_SubscribeOptionsBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateSubscribeOptionsBuilder();
|
||||
|
||||
[TestMethod]
|
||||
public void Create_SubscribeOptionsBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateSubscribeOptionsBuilder();
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Create_UnsubscribeOptionsBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateUnsubscribeOptionsBuilder();
|
||||
|
||||
[TestMethod]
|
||||
public void Create_UnsubscribeOptionsBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateUnsubscribeOptionsBuilder();
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Create_TopicFilterBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateTopicFilterBuilder();
|
||||
|
||||
[TestMethod]
|
||||
public void Create_TopicFilterBuilder()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var builder = factory.CreateTopicFilterBuilder();
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(builder);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Create_MqttServer()
|
||||
{
|
||||
var factory = new MqttServerFactory();
|
||||
var server = factory.CreateMqttServer(new MqttServerOptionsBuilder().Build());
|
||||
|
||||
[TestMethod]
|
||||
public void Create_MqttServer()
|
||||
{
|
||||
var factory = new MqttServerFactory();
|
||||
var server = factory.CreateMqttServer(new MqttServerOptionsBuilder().Build());
|
||||
Assert.IsNotNull(server);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(server);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Create_MqttClient()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var client = factory.CreateMqttClient();
|
||||
|
||||
[TestMethod]
|
||||
public void Create_MqttClient()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var client = factory.CreateMqttClient();
|
||||
Assert.IsNotNull(client);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(client);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Create_LowLevelMqttClient()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var client = factory.CreateLowLevelMqttClient();
|
||||
|
||||
[TestMethod]
|
||||
public void Create_LowLevelMqttClient()
|
||||
{
|
||||
var factory = new MqttClientFactory();
|
||||
var client = factory.CreateLowLevelMqttClient();
|
||||
|
||||
Assert.IsNotNull(client);
|
||||
}
|
||||
Assert.IsNotNull(client);
|
||||
}
|
||||
}
|
||||
@@ -9,303 +9,303 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter
|
||||
namespace MQTTnet.Tests.Formatter;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttBufferReader_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttBufferReader_Tests
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException), "Expected at least 4 bytes but there are only 3 bytes")]
|
||||
public void Fire_Exception_If_Not_Enough_Data()
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException), "Expected at least 4 bytes but there are only 3 bytes")]
|
||||
public void Fire_Exception_If_Not_Enough_Data()
|
||||
var buffer = new byte[] { 0, 1, 2 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
reader.SetBuffer(buffer, 0, 3);
|
||||
|
||||
// 1 byte is missing.
|
||||
reader.ReadFourByteInteger();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException), "Expected at least 4 bytes but there are only 3 bytes")]
|
||||
public void Fire_Exception_If_Not_Enough_Data_With_Longer_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
reader.SetBuffer(buffer, 0, 3);
|
||||
|
||||
// 1 byte is missing.
|
||||
reader.ReadFourByteInteger();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Is_EndOfStream_Without_Buffer()
|
||||
{
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
Assert.IsTrue(reader.EndOfStream);
|
||||
Assert.AreEqual(0, reader.BytesLeft);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_Remaining_Data_From_Larger_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
// The used buffer contains more data than used!
|
||||
reader.SetBuffer(buffer, 0, 5);
|
||||
|
||||
// This should only read 5 bytes even if more data is in the buffer
|
||||
// due to custom bounds.
|
||||
var remainingData = reader.ReadRemainingData();
|
||||
|
||||
Assert.IsTrue(reader.EndOfStream);
|
||||
Assert.AreEqual(0, reader.BytesLeft);
|
||||
Assert.AreEqual(5, remainingData.Length);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void Read_Various_Positions_and_Offsets()
|
||||
{
|
||||
const int numBufferElements = 1000;
|
||||
const int numTestSequences = 10000;
|
||||
|
||||
const string testString = "The quick brown fox jumps over the lazy dog.";
|
||||
|
||||
var rndSeed = Environment.TickCount;
|
||||
// rndSeed = ...; // assign random seed here if there is an error so that it can be repeated
|
||||
var rnd = new Random(rndSeed);
|
||||
// rndSeed is printed when an assert fails
|
||||
|
||||
var bufferElements = new List<ElementReference>();
|
||||
|
||||
byte[] elementBuffer;
|
||||
|
||||
// Fill a buffer with strings or numbers in big endian format
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
reader.SetBuffer(buffer, 0, 3);
|
||||
|
||||
// 1 byte is missing.
|
||||
reader.ReadFourByteInteger();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException), "Expected at least 4 bytes but there are only 3 bytes")]
|
||||
public void Fire_Exception_If_Not_Enough_Data_With_Longer_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
reader.SetBuffer(buffer, 0, 3);
|
||||
|
||||
// 1 byte is missing.
|
||||
reader.ReadFourByteInteger();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Is_EndOfStream_Without_Buffer()
|
||||
{
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
Assert.IsTrue(reader.EndOfStream);
|
||||
Assert.AreEqual(0, reader.BytesLeft);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Read_Remaining_Data_From_Larger_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
// The used buffer contains more data than used!
|
||||
reader.SetBuffer(buffer, 0, 5);
|
||||
|
||||
// This should only read 5 bytes even if more data is in the buffer
|
||||
// due to custom bounds.
|
||||
var remainingData = reader.ReadRemainingData();
|
||||
|
||||
Assert.IsTrue(reader.EndOfStream);
|
||||
Assert.AreEqual(0, reader.BytesLeft);
|
||||
Assert.AreEqual(5, remainingData.Length);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void Read_Various_Positions_and_Offsets()
|
||||
{
|
||||
const int NumBufferElements = 1000;
|
||||
const int NumTestSequences = 10000;
|
||||
|
||||
const string TestString = "The quick brown fox jumps over the lazy dog.";
|
||||
|
||||
var rndSeed = Environment.TickCount;
|
||||
// rndSeed = ...; // assign random seed here if there is an error so that it can be repeated
|
||||
var rnd = new Random(rndSeed);
|
||||
// rndSeed is printed when an assert fails
|
||||
|
||||
var bufferElements = new List<ElementReference>();
|
||||
|
||||
byte[] elementBuffer;
|
||||
|
||||
// Fill a buffer with strings or numbers in big endian format
|
||||
using (var ms = new MemoryStream())
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
var bufferOffset = 0;
|
||||
|
||||
for (var i = 0; i < numBufferElements; i++)
|
||||
{
|
||||
var bufferOffset = 0;
|
||||
var nextElementType = (ElementReference.BufferElementType)rnd.Next(ElementReference.NumBufferElementTypes);
|
||||
|
||||
for (var i = 0; i < NumBufferElements; i++)
|
||||
{
|
||||
var nextElementType = (ElementReference.BufferElementType)rnd.Next(ElementReference.NumBufferElementTypes);
|
||||
|
||||
byte[] elementBytes = null;
|
||||
uint elementNumberValue = 0; // all element number values fit into uint
|
||||
string elementStringValue = null;
|
||||
var elementSize = 0;
|
||||
|
||||
var alreadyBigEndian = false;
|
||||
switch (nextElementType)
|
||||
{
|
||||
case ElementReference.BufferElementType.Byte:
|
||||
var byteValue = (byte)i;
|
||||
elementNumberValue = byteValue;
|
||||
elementSize = sizeof(byte);
|
||||
elementBytes = new byte[1] { byteValue };
|
||||
alreadyBigEndian = true; // nothing to swap
|
||||
break;
|
||||
case ElementReference.BufferElementType.TwoByteInt:
|
||||
var ushortValue = (ushort)i;
|
||||
elementNumberValue = ushortValue;
|
||||
elementSize = sizeof(ushort);
|
||||
elementBytes = BitConverter.GetBytes(ushortValue);
|
||||
break;
|
||||
case ElementReference.BufferElementType.FourByteInt:
|
||||
var uintValue = (uint)i;
|
||||
elementNumberValue = uintValue;
|
||||
elementSize = sizeof(uint);
|
||||
elementBytes = BitConverter.GetBytes(uintValue);
|
||||
break;
|
||||
case ElementReference.BufferElementType.VariableSizeInt:
|
||||
{
|
||||
elementNumberValue = (uint)i;
|
||||
var writer = new MqttBufferWriter(4, 4);
|
||||
writer.WriteVariableByteInteger(elementNumberValue);
|
||||
elementSize = writer.Length;
|
||||
elementBytes = new byte[elementSize];
|
||||
var buffer = writer.GetBuffer();
|
||||
Array.Copy(buffer, elementBytes, elementSize);
|
||||
alreadyBigEndian = true; // nothing to swap
|
||||
}
|
||||
break;
|
||||
case ElementReference.BufferElementType.String:
|
||||
{
|
||||
var stringLen = rnd.Next(TestString.Length);
|
||||
elementStringValue = TestString.Substring(0, stringLen); // could be empty
|
||||
var writer = new MqttBufferWriter(stringLen + 1, stringLen + 1);
|
||||
writer.WriteString(elementStringValue);
|
||||
elementSize = writer.Length;
|
||||
elementBytes = new byte[elementSize];
|
||||
var buffer = writer.GetBuffer();
|
||||
Array.Copy(buffer, elementBytes, elementSize);
|
||||
alreadyBigEndian = true; // nothing to swap
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (BitConverter.IsLittleEndian && !alreadyBigEndian)
|
||||
{
|
||||
Array.Reverse(elementBytes);
|
||||
}
|
||||
|
||||
bw.Write(elementBytes);
|
||||
bufferElements.Add(new ElementReference(nextElementType, elementSize, elementNumberValue, elementStringValue, bufferOffset));
|
||||
bufferOffset += elementSize;
|
||||
}
|
||||
|
||||
elementBuffer = ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < NumTestSequences; i++)
|
||||
{
|
||||
var firstElementIndex = rnd.Next(NumBufferElements - 1);
|
||||
// segmentElementIndex will be < NumBufferElements - 1, which means there is at least one byte left for segment element count
|
||||
var elementCount = rnd.Next(NumBufferElements - firstElementIndex) + 1;
|
||||
var lastElementIndex = firstElementIndex + elementCount - 1;
|
||||
// Use element index to get buffer offsets
|
||||
var lastElement = bufferElements[lastElementIndex];
|
||||
var segmentStartPosition = bufferElements[firstElementIndex].BufferOffset;
|
||||
var segmentEndPosition = lastElement.BufferOffset + lastElement.Size;
|
||||
var segmentLength = segmentEndPosition - segmentStartPosition;
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(elementBuffer, segmentStartPosition, segmentLength);
|
||||
|
||||
// read all elements in the buffer segment; values should be as expected
|
||||
for (var n = 0; n < elementCount; n++)
|
||||
{
|
||||
var element = bufferElements[firstElementIndex + n];
|
||||
uint elementNumberValue = 0;
|
||||
byte[] elementBytes = null;
|
||||
uint elementNumberValue = 0; // all element number values fit into uint
|
||||
string elementStringValue = null;
|
||||
var elementSize = 0;
|
||||
|
||||
var expectedPosition = element.BufferOffset - segmentStartPosition;
|
||||
Assert.AreEqual(expectedPosition, reader.Position);
|
||||
|
||||
switch (element.Type)
|
||||
var alreadyBigEndian = false;
|
||||
switch (nextElementType)
|
||||
{
|
||||
case ElementReference.BufferElementType.Byte:
|
||||
{
|
||||
elementNumberValue = reader.ReadByte();
|
||||
}
|
||||
var byteValue = (byte)i;
|
||||
elementNumberValue = byteValue;
|
||||
elementSize = sizeof(byte);
|
||||
elementBytes = [byteValue];
|
||||
alreadyBigEndian = true; // nothing to swap
|
||||
break;
|
||||
case ElementReference.BufferElementType.TwoByteInt:
|
||||
{
|
||||
elementNumberValue = reader.ReadTwoByteInteger();
|
||||
}
|
||||
var ushortValue = (ushort)i;
|
||||
elementNumberValue = ushortValue;
|
||||
elementSize = sizeof(ushort);
|
||||
elementBytes = BitConverter.GetBytes(ushortValue);
|
||||
break;
|
||||
case ElementReference.BufferElementType.FourByteInt:
|
||||
{
|
||||
elementNumberValue = reader.ReadFourByteInteger();
|
||||
}
|
||||
var uintValue = (uint)i;
|
||||
elementNumberValue = uintValue;
|
||||
elementSize = sizeof(uint);
|
||||
elementBytes = BitConverter.GetBytes(uintValue);
|
||||
break;
|
||||
case ElementReference.BufferElementType.VariableSizeInt:
|
||||
{
|
||||
elementNumberValue = reader.ReadVariableByteInteger();
|
||||
elementNumberValue = (uint)i;
|
||||
var writer = new MqttBufferWriter(4, 4);
|
||||
writer.WriteVariableByteInteger(elementNumberValue);
|
||||
elementSize = writer.Length;
|
||||
elementBytes = new byte[elementSize];
|
||||
var buffer = writer.GetBuffer();
|
||||
Array.Copy(buffer, elementBytes, elementSize);
|
||||
alreadyBigEndian = true; // nothing to swap
|
||||
}
|
||||
break;
|
||||
case ElementReference.BufferElementType.String:
|
||||
{
|
||||
elementStringValue = reader.ReadString();
|
||||
var stringLen = rnd.Next(testString.Length);
|
||||
elementStringValue = testString[..stringLen]; // could be empty
|
||||
var writer = new MqttBufferWriter(stringLen + 1, stringLen + 1);
|
||||
writer.WriteString(elementStringValue);
|
||||
elementSize = writer.Length;
|
||||
elementBytes = new byte[elementSize];
|
||||
var buffer = writer.GetBuffer();
|
||||
Array.Copy(buffer, elementBytes, elementSize);
|
||||
alreadyBigEndian = true; // nothing to swap
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (elementStringValue != null)
|
||||
if (BitConverter.IsLittleEndian && !alreadyBigEndian)
|
||||
{
|
||||
Assert.AreEqual(elementStringValue, element.StringValue, $"'{elementStringValue}' not equal '{element.StringValue}' with random seed {rndSeed}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(elementNumberValue, element.NumberValue, $"{elementNumberValue} not equal {element.NumberValue} with random seed {rndSeed}.");
|
||||
Array.Reverse(elementBytes);
|
||||
}
|
||||
|
||||
bw.Write(elementBytes);
|
||||
bufferElements.Add(new ElementReference(nextElementType, elementSize, elementNumberValue, elementStringValue, bufferOffset));
|
||||
bufferOffset += elementSize;
|
||||
}
|
||||
|
||||
// confirm end of stream
|
||||
Assert.AreEqual(0, reader.BytesLeft, "BytesLeft not zero as expected with random seed " + rndSeed);
|
||||
Assert.IsTrue(reader.EndOfStream, "Not end of stream as expected with random seed " + rndSeed);
|
||||
elementBuffer = ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Report_Correct_Length_For_Full_Buffer()
|
||||
for (var i = 0; i < numTestSequences; i++)
|
||||
{
|
||||
var buffer = new byte[] { 5, 6, 7, 8, 9 };
|
||||
var firstElementIndex = rnd.Next(numBufferElements - 1);
|
||||
// segmentElementIndex will be < NumBufferElements - 1, which means there is at least one byte left for segment element count
|
||||
var elementCount = rnd.Next(numBufferElements - firstElementIndex) + 1;
|
||||
var lastElementIndex = firstElementIndex + elementCount - 1;
|
||||
// Use element index to get buffer offsets
|
||||
var lastElement = bufferElements[lastElementIndex];
|
||||
var segmentStartPosition = bufferElements[firstElementIndex].BufferOffset;
|
||||
var segmentEndPosition = lastElement.BufferOffset + lastElement.Size;
|
||||
var segmentLength = segmentEndPosition - segmentStartPosition;
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(buffer, 0, 5);
|
||||
reader.SetBuffer(elementBuffer, segmentStartPosition, segmentLength);
|
||||
|
||||
Assert.IsFalse(reader.EndOfStream);
|
||||
Assert.AreEqual(5, reader.BytesLeft);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Report_Correct_Length_For_Partial_End_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
// The used buffer contains more data than used!
|
||||
reader.SetBuffer(buffer, 5, 5);
|
||||
|
||||
Assert.IsFalse(reader.EndOfStream);
|
||||
Assert.AreEqual(5, reader.BytesLeft);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Report_Correct_Length_For_Partial_Start_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
// The used buffer contains more data than used!
|
||||
reader.SetBuffer(buffer, 0, 5);
|
||||
|
||||
Assert.IsFalse(reader.EndOfStream);
|
||||
Assert.AreEqual(5, reader.BytesLeft);
|
||||
}
|
||||
|
||||
|
||||
// Helper class to build up a reference to elements of various types in a buffer
|
||||
class ElementReference
|
||||
{
|
||||
public enum BufferElementType
|
||||
// read all elements in the buffer segment; values should be as expected
|
||||
for (var n = 0; n < elementCount; n++)
|
||||
{
|
||||
Byte,
|
||||
TwoByteInt,
|
||||
FourByteInt,
|
||||
VariableSizeInt,
|
||||
String
|
||||
var element = bufferElements[firstElementIndex + n];
|
||||
uint elementNumberValue = 0;
|
||||
string elementStringValue = null;
|
||||
|
||||
var expectedPosition = element.BufferOffset - segmentStartPosition;
|
||||
Assert.AreEqual(expectedPosition, reader.Position);
|
||||
|
||||
switch (element.Type)
|
||||
{
|
||||
case ElementReference.BufferElementType.Byte:
|
||||
{
|
||||
elementNumberValue = reader.ReadByte();
|
||||
}
|
||||
break;
|
||||
case ElementReference.BufferElementType.TwoByteInt:
|
||||
{
|
||||
elementNumberValue = reader.ReadTwoByteInteger();
|
||||
}
|
||||
break;
|
||||
case ElementReference.BufferElementType.FourByteInt:
|
||||
{
|
||||
elementNumberValue = reader.ReadFourByteInteger();
|
||||
}
|
||||
break;
|
||||
case ElementReference.BufferElementType.VariableSizeInt:
|
||||
{
|
||||
elementNumberValue = reader.ReadVariableByteInteger();
|
||||
}
|
||||
break;
|
||||
case ElementReference.BufferElementType.String:
|
||||
{
|
||||
elementStringValue = reader.ReadString();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (elementStringValue != null)
|
||||
{
|
||||
Assert.AreEqual(elementStringValue, element.StringValue, $"'{elementStringValue}' not equal '{element.StringValue}' with random seed {rndSeed}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(elementNumberValue, element.NumberValue, $"{elementNumberValue} not equal {element.NumberValue} with random seed {rndSeed}.");
|
||||
}
|
||||
}
|
||||
|
||||
public const int NumBufferElementTypes = 5;
|
||||
|
||||
public ElementReference(BufferElementType type, int size, uint numberValue, string stringValue, int bufferOffset)
|
||||
{
|
||||
Type = type;
|
||||
Size = size;
|
||||
NumberValue = numberValue;
|
||||
StringValue = stringValue;
|
||||
BufferOffset = bufferOffset;
|
||||
}
|
||||
|
||||
public int BufferOffset { get; }
|
||||
public uint NumberValue { get; }
|
||||
public int Size { get; }
|
||||
public string StringValue { get; }
|
||||
public BufferElementType Type { get; }
|
||||
// confirm end of stream
|
||||
Assert.AreEqual(0, reader.BytesLeft, "BytesLeft not zero as expected with random seed " + rndSeed);
|
||||
Assert.IsTrue(reader.EndOfStream, "Not end of stream as expected with random seed " + rndSeed);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Report_Correct_Length_For_Full_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 5, 6, 7, 8, 9 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(buffer, 0, 5);
|
||||
|
||||
Assert.IsFalse(reader.EndOfStream);
|
||||
Assert.AreEqual(5, reader.BytesLeft);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Report_Correct_Length_For_Partial_End_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
// The used buffer contains more data than used!
|
||||
reader.SetBuffer(buffer, 5, 5);
|
||||
|
||||
Assert.IsFalse(reader.EndOfStream);
|
||||
Assert.AreEqual(5, reader.BytesLeft);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Report_Correct_Length_For_Partial_Start_Buffer()
|
||||
{
|
||||
var buffer = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
|
||||
// The used buffer contains more data than used!
|
||||
reader.SetBuffer(buffer, 0, 5);
|
||||
|
||||
Assert.IsFalse(reader.EndOfStream);
|
||||
Assert.AreEqual(5, reader.BytesLeft);
|
||||
}
|
||||
|
||||
|
||||
// Helper class to build up a reference to elements of various types in a buffer
|
||||
class ElementReference
|
||||
{
|
||||
public enum BufferElementType
|
||||
{
|
||||
Byte,
|
||||
TwoByteInt,
|
||||
FourByteInt,
|
||||
VariableSizeInt,
|
||||
String
|
||||
}
|
||||
|
||||
public const int NumBufferElementTypes = 5;
|
||||
|
||||
public ElementReference(BufferElementType type, int size, uint numberValue, string stringValue, int bufferOffset)
|
||||
{
|
||||
Type = type;
|
||||
Size = size;
|
||||
NumberValue = numberValue;
|
||||
StringValue = stringValue;
|
||||
BufferOffset = bufferOffset;
|
||||
}
|
||||
|
||||
public int BufferOffset { get; }
|
||||
public uint NumberValue { get; }
|
||||
public int Size { get; }
|
||||
public string StringValue { get; }
|
||||
public BufferElementType Type { get; }
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,494 +4,437 @@
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter
|
||||
namespace MQTTnet.Tests.Formatter;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttPacketSerialization_V3_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketSerialization_V3_Tests
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttAuthPacket_V311()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttAuthPacket_V311()
|
||||
{
|
||||
var authPacket = new MqttAuthPacket();
|
||||
Assert.ThrowsException<MqttProtocolViolationException>(() => MqttPacketSerializationHelper.EncodeAndDecodePacket(authPacket, MqttProtocolVersion.V311));
|
||||
}
|
||||
var authPacket = new MqttAuthPacket();
|
||||
Assert.ThrowsException<MqttProtocolViolationException>(() => MqttPacketSerializationHelper.EncodeAndDecodePacket(authPacket, MqttProtocolVersion.V311));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttConnAckPacket_V311()
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttConnAckPacket_V311()
|
||||
{
|
||||
var connAckPacket = new MqttConnAckPacket
|
||||
{
|
||||
var connAckPacket = new MqttConnAckPacket
|
||||
{
|
||||
AuthenticationData = Encoding.UTF8.GetBytes("AuthenticationData"),
|
||||
AuthenticationMethod = "AuthenticationMethod",
|
||||
ReasonCode = MqttConnectReasonCode.ServerUnavailable,
|
||||
ReasonString = "ReasonString",
|
||||
ReceiveMaximum = 123,
|
||||
ResponseInformation = "ResponseInformation",
|
||||
RetainAvailable = true,
|
||||
ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized,
|
||||
ServerReference = "ServerReference",
|
||||
AssignedClientIdentifier = "AssignedClientIdentifier",
|
||||
IsSessionPresent = true,
|
||||
MaximumPacketSize = 456,
|
||||
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce,
|
||||
ServerKeepAlive = 789,
|
||||
SessionExpiryInterval = 852,
|
||||
SharedSubscriptionAvailable = true,
|
||||
SubscriptionIdentifiersAvailable = true,
|
||||
TopicAliasMaximum = 963,
|
||||
WildcardSubscriptionAvailable = true,
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
AuthenticationData = "AuthenticationData"u8.ToArray(),
|
||||
AuthenticationMethod = "AuthenticationMethod",
|
||||
ReasonCode = MqttConnectReasonCode.ServerUnavailable,
|
||||
ReasonString = "ReasonString",
|
||||
ReceiveMaximum = 123,
|
||||
ResponseInformation = "ResponseInformation",
|
||||
RetainAvailable = true,
|
||||
ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized,
|
||||
ServerReference = "ServerReference",
|
||||
AssignedClientIdentifier = "AssignedClientIdentifier",
|
||||
IsSessionPresent = true,
|
||||
MaximumPacketSize = 456,
|
||||
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce,
|
||||
ServerKeepAlive = 789,
|
||||
SessionExpiryInterval = 852,
|
||||
SharedSubscriptionAvailable = true,
|
||||
SubscriptionIdentifiersAvailable = true,
|
||||
TopicAliasMaximum = 963,
|
||||
WildcardSubscriptionAvailable = true,
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connAckPacket, MqttProtocolVersion.V311);
|
||||
|
||||
CollectionAssert.AreEqual(null, deserialized.AuthenticationData); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AuthenticationMethod); // Not supported in v3.1.1
|
||||
//Assert.AreEqual(connAckPacket.ReasonCode, deserialized.ReasonCode);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.ReceiveMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.ResponseInformation); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.RetainAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttConnectReturnCode.ConnectionRefusedNotAuthorized, deserialized.ReturnCode);
|
||||
Assert.AreEqual(null, deserialized.ServerReference); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AssignedClientIdentifier); // Not supported in v3.1.1
|
||||
Assert.AreEqual(connAckPacket.IsSessionPresent, deserialized.IsSessionPresent);
|
||||
Assert.AreEqual(0U, deserialized.MaximumPacketSize); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, deserialized.MaximumQoS); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.ServerKeepAlive); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.SessionExpiryInterval); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.SharedSubscriptionAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.SubscriptionIdentifiersAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.TopicAliasMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.WildcardSubscriptionAvailable);
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttConnAckPacket_V310()
|
||||
{
|
||||
var connAckPacket = new MqttConnAckPacket
|
||||
{
|
||||
AuthenticationData = "AuthenticationData"u8.ToArray(),
|
||||
AuthenticationMethod = "AuthenticationMethod",
|
||||
ReasonCode = MqttConnectReasonCode.ServerUnavailable,
|
||||
ReasonString = "ReasonString",
|
||||
ReceiveMaximum = 123,
|
||||
ResponseInformation = "ResponseInformation",
|
||||
RetainAvailable = true,
|
||||
ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized,
|
||||
ServerReference = "ServerReference",
|
||||
AssignedClientIdentifier = "AssignedClientIdentifier",
|
||||
IsSessionPresent = true,
|
||||
MaximumPacketSize = 456,
|
||||
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce,
|
||||
ServerKeepAlive = 789,
|
||||
SessionExpiryInterval = 852,
|
||||
SharedSubscriptionAvailable = true,
|
||||
SubscriptionIdentifiersAvailable = true,
|
||||
TopicAliasMaximum = 963,
|
||||
WildcardSubscriptionAvailable = true,
|
||||
UserProperties = [new MqttUserProperty("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connAckPacket, MqttProtocolVersion.V310);
|
||||
|
||||
CollectionAssert.AreEqual(null, deserialized.AuthenticationData); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AuthenticationMethod); // Not supported in v3.1.1
|
||||
//Assert.AreEqual(connAckPacket.ReasonCode, deserialized.ReasonCode);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.ReceiveMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.ResponseInformation); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.RetainAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttConnectReturnCode.ConnectionRefusedNotAuthorized, deserialized.ReturnCode);
|
||||
Assert.AreEqual(null, deserialized.ServerReference); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AssignedClientIdentifier); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.IsSessionPresent); // Not supported in v3.1.0 <- !
|
||||
Assert.AreEqual(0U, deserialized.MaximumPacketSize); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, deserialized.MaximumQoS); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.ServerKeepAlive); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.SessionExpiryInterval); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.SharedSubscriptionAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.SubscriptionIdentifiersAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.TopicAliasMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.WildcardSubscriptionAvailable);
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttConnectPacket_V311()
|
||||
{
|
||||
var connectPacket = new MqttConnectPacket
|
||||
{
|
||||
Username = "Username",
|
||||
Password = "Password"u8.ToArray(),
|
||||
ClientId = "ClientId",
|
||||
AuthenticationData = "AuthenticationData"u8.ToArray(),
|
||||
AuthenticationMethod = "AuthenticationMethod",
|
||||
CleanSession = true,
|
||||
ReceiveMaximum = 123,
|
||||
WillFlag = true,
|
||||
WillTopic = "WillTopic",
|
||||
WillMessage = "WillMessage"u8.ToArray(),
|
||||
WillRetain = true,
|
||||
KeepAlivePeriod = 456,
|
||||
MaximumPacketSize = 789,
|
||||
RequestProblemInformation = true,
|
||||
RequestResponseInformation = true,
|
||||
SessionExpiryInterval = 27,
|
||||
TopicAliasMaximum = 67,
|
||||
WillContentType = "WillContentType",
|
||||
WillCorrelationData = "WillCorrelationData"u8.ToArray(),
|
||||
WillDelayInterval = 782,
|
||||
WillQoS = MqttQualityOfServiceLevel.ExactlyOnce,
|
||||
WillResponseTopic = "WillResponseTopic",
|
||||
WillMessageExpiryInterval = 542,
|
||||
WillPayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData,
|
||||
UserProperties = [new("Foo", "Bar")],
|
||||
WillUserProperties = [new("WillFoo", "WillBar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connectPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(connectPacket.Username, deserialized.Username);
|
||||
CollectionAssert.AreEqual(connectPacket.Password, deserialized.Password);
|
||||
Assert.AreEqual(connectPacket.ClientId, deserialized.ClientId);
|
||||
CollectionAssert.AreEqual(null, deserialized.AuthenticationData); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AuthenticationMethod); // Not supported in v3.1.1
|
||||
Assert.AreEqual(connectPacket.CleanSession, deserialized.CleanSession);
|
||||
Assert.AreEqual(0L, deserialized.ReceiveMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(connectPacket.WillFlag, deserialized.WillFlag);
|
||||
Assert.AreEqual(connectPacket.WillTopic, deserialized.WillTopic);
|
||||
CollectionAssert.AreEqual(connectPacket.WillMessage, deserialized.WillMessage);
|
||||
Assert.AreEqual(connectPacket.WillRetain, deserialized.WillRetain);
|
||||
Assert.AreEqual(connectPacket.KeepAlivePeriod, deserialized.KeepAlivePeriod);
|
||||
// MaximumPacketSize not available in MQTTv3.
|
||||
// RequestProblemInformation not available in MQTTv3.
|
||||
// RequestResponseInformation not available in MQTTv3.
|
||||
// SessionExpiryInterval not available in MQTTv3.
|
||||
// TopicAliasMaximum not available in MQTTv3.
|
||||
// WillContentType not available in MQTTv3.
|
||||
// WillCorrelationData not available in MQTTv3.
|
||||
// WillDelayInterval not available in MQTTv3.
|
||||
Assert.AreEqual(connectPacket.WillQoS, deserialized.WillQoS);
|
||||
// WillResponseTopic not available in MQTTv3.
|
||||
// WillMessageExpiryInterval not available in MQTTv3.
|
||||
// WillPayloadFormatIndicator not available in MQTTv3.
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
Assert.IsNull(deserialized.WillUserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttDisconnectPacket_V311()
|
||||
{
|
||||
var disconnectPacket = new MqttDisconnectPacket
|
||||
{
|
||||
ReasonCode = MqttDisconnectReasonCode.NormalDisconnection, // MQTTv3 has no other values than this.
|
||||
ReasonString = "ReasonString",
|
||||
ServerReference = "ServerReference",
|
||||
SessionExpiryInterval = 234,
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(disconnectPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(disconnectPacket.ReasonCode, deserialized.ReasonCode);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.ServerReference); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.SessionExpiryInterval); // Not supported in v3.1.1
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPingReqPacket_V311()
|
||||
{
|
||||
var pingReqPacket = new MqttPingReqPacket();
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pingReqPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.IsNotNull(deserialized);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPingRespPacket_V311()
|
||||
{
|
||||
var pingRespPacket = new MqttPingRespPacket();
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pingRespPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.IsNotNull(deserialized);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPubAckPacket_V311()
|
||||
{
|
||||
var pubAckPacket = new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCode = MqttPubAckReasonCode.NoMatchingSubscribers,
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubAckPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(pubAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(MqttPubAckReasonCode.Success, deserialized.ReasonCode); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPubCompPacket_V311()
|
||||
{
|
||||
var pubCompPacket = new MqttPubCompPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCode = MqttPubCompReasonCode.PacketIdentifierNotFound,
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubCompPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(pubCompPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
// ReasonCode not available in MQTTv3.
|
||||
// ReasonString not available in MQTTv3.
|
||||
// UserProperties not available in MQTTv3.
|
||||
Assert.IsNull(deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPublishPacket_V311()
|
||||
{
|
||||
var publishPacket = new MqttPublishPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
Dup = true,
|
||||
Retain = true,
|
||||
PayloadSegment = new ArraySegment<byte>("Payload"u8.ToArray()),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||
Topic = "Topic",
|
||||
ResponseTopic = "/Response",
|
||||
ContentType = "Content-Type",
|
||||
CorrelationData = "CorrelationData"u8.ToArray(),
|
||||
TopicAlias = 27,
|
||||
SubscriptionIdentifiers = [123],
|
||||
MessageExpiryInterval = 38,
|
||||
PayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData,
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(publishPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
|
||||
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
|
||||
CollectionAssert.AreEqual(publishPacket.Payload.ToArray(), deserialized.Payload.ToArray());
|
||||
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
|
||||
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
|
||||
Assert.AreEqual(null, deserialized.ResponseTopic); // Not supported in v3.1.1.
|
||||
Assert.AreEqual(null, deserialized.ContentType); // Not supported in v3.1.1.
|
||||
CollectionAssert.AreEqual(null, deserialized.CorrelationData); // Not supported in v3.1.1.
|
||||
Assert.AreEqual(0U, deserialized.TopicAlias); // Not supported in v3.1.1.
|
||||
CollectionAssert.AreEqual(null, deserialized.SubscriptionIdentifiers); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.MessageExpiryInterval); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttPayloadFormatIndicator.Unspecified, deserialized.PayloadFormatIndicator); // Not supported in v3.1.1
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPubRecPacket_V311()
|
||||
{
|
||||
var pubRecPacket = new MqttPubRecPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCode = MqttPubRecReasonCode.UnspecifiedError,
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRecPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(pubRecPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
// ReasonCode not available in MQTTv3.
|
||||
// ReasonString not available in MQTTv3.
|
||||
// UserProperties not available in MQTTv3.
|
||||
Assert.IsNull(deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPubRelPacket_V311()
|
||||
{
|
||||
var pubRelPacket = new MqttPubRelPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound,
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRelPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(pubRelPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
// ReasonCode not available in MQTTv3.
|
||||
// ReasonString not available in MQTTv3.
|
||||
// UserProperties not available in MQTTv3.
|
||||
Assert.IsNull(deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttSubAckPacket_V311()
|
||||
{
|
||||
var subAckPacket = new MqttSubAckPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonString = "ReasonString",
|
||||
ReasonCodes = [MqttSubscribeReasonCode.GrantedQoS1],
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subAckPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(subAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
Assert.AreEqual(subAckPacket.ReasonCodes.Count, deserialized.ReasonCodes.Count);
|
||||
Assert.AreEqual(subAckPacket.ReasonCodes[0], deserialized.ReasonCodes[0]);
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttSubscribePacket_V311()
|
||||
{
|
||||
var subscribePacket = new MqttSubscribePacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
SubscriptionIdentifier = 456,
|
||||
TopicFilters =
|
||||
[
|
||||
new()
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
Topic = "Topic",
|
||||
NoLocal = true,
|
||||
RetainHandling = MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly,
|
||||
RetainAsPublished = true,
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
||||
}
|
||||
};
|
||||
],
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connAckPacket, MqttProtocolVersion.V311);
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subscribePacket, MqttProtocolVersion.V311);
|
||||
|
||||
CollectionAssert.AreEqual(null, deserialized.AuthenticationData); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AuthenticationMethod); // Not supported in v3.1.1
|
||||
//Assert.AreEqual(connAckPacket.ReasonCode, deserialized.ReasonCode);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.ReceiveMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.ResponseInformation); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.RetainAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttConnectReturnCode.ConnectionRefusedNotAuthorized, deserialized.ReturnCode);
|
||||
Assert.AreEqual(null, deserialized.ServerReference); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AssignedClientIdentifier); // Not supported in v3.1.1
|
||||
Assert.AreEqual(connAckPacket.IsSessionPresent, deserialized.IsSessionPresent);
|
||||
Assert.AreEqual(0U, deserialized.MaximumPacketSize); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, deserialized.MaximumQoS); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.ServerKeepAlive); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.SessionExpiryInterval); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.SharedSubscriptionAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.SubscriptionIdentifiersAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.TopicAliasMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.WildcardSubscriptionAvailable);
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttConnAckPacket_V310()
|
||||
Assert.AreEqual(subscribePacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(0U, deserialized.SubscriptionIdentifier); // Not supported in v3.1.1
|
||||
Assert.AreEqual(1, deserialized.TopicFilters.Count);
|
||||
Assert.AreEqual(subscribePacket.TopicFilters[0].Topic, deserialized.TopicFilters[0].Topic);
|
||||
Assert.AreEqual(false, deserialized.TopicFilters[0].NoLocal); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttRetainHandling.SendAtSubscribe, deserialized.TopicFilters[0].RetainHandling); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.TopicFilters[0].RetainAsPublished); // Not supported in v3.1.1
|
||||
Assert.AreEqual(subscribePacket.TopicFilters[0].QualityOfServiceLevel, deserialized.TopicFilters[0].QualityOfServiceLevel);
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttUnsubAckPacket_V311()
|
||||
{
|
||||
var unsubAckPacket = new MqttUnsubAckPacket
|
||||
{
|
||||
var connAckPacket = new MqttConnAckPacket
|
||||
{
|
||||
AuthenticationData = Encoding.UTF8.GetBytes("AuthenticationData"),
|
||||
AuthenticationMethod = "AuthenticationMethod",
|
||||
ReasonCode = MqttConnectReasonCode.ServerUnavailable,
|
||||
ReasonString = "ReasonString",
|
||||
ReceiveMaximum = 123,
|
||||
ResponseInformation = "ResponseInformation",
|
||||
RetainAvailable = true,
|
||||
ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized,
|
||||
ServerReference = "ServerReference",
|
||||
AssignedClientIdentifier = "AssignedClientIdentifier",
|
||||
IsSessionPresent = true,
|
||||
MaximumPacketSize = 456,
|
||||
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce,
|
||||
ServerKeepAlive = 789,
|
||||
SessionExpiryInterval = 852,
|
||||
SharedSubscriptionAvailable = true,
|
||||
SubscriptionIdentifiersAvailable = true,
|
||||
TopicAliasMaximum = 963,
|
||||
WildcardSubscriptionAvailable = true,
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
PacketIdentifier = 123,
|
||||
ReasonCodes = [MqttUnsubscribeReasonCode.ImplementationSpecificError],
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connAckPacket, MqttProtocolVersion.V310);
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubAckPacket, MqttProtocolVersion.V311);
|
||||
|
||||
CollectionAssert.AreEqual(null, deserialized.AuthenticationData); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AuthenticationMethod); // Not supported in v3.1.1
|
||||
//Assert.AreEqual(connAckPacket.ReasonCode, deserialized.ReasonCode);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.ReceiveMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.ResponseInformation); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.RetainAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttConnectReturnCode.ConnectionRefusedNotAuthorized, deserialized.ReturnCode);
|
||||
Assert.AreEqual(null, deserialized.ServerReference); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AssignedClientIdentifier); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.IsSessionPresent); // Not supported in v3.1.0 <- !
|
||||
Assert.AreEqual(0U, deserialized.MaximumPacketSize); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, deserialized.MaximumQoS); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.ServerKeepAlive); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.SessionExpiryInterval); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.SharedSubscriptionAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.SubscriptionIdentifiersAvailable); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.TopicAliasMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.WildcardSubscriptionAvailable);
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
Assert.AreEqual(unsubAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
CollectionAssert.AreEqual(null, deserialized.ReasonCodes); // Not supported in v3.1.1
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttConnectPacket_V311()
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttUnsubscribePacket_V311()
|
||||
{
|
||||
var unsubscribePacket = new MqttUnsubscribePacket
|
||||
{
|
||||
var connectPacket = new MqttConnectPacket
|
||||
{
|
||||
Username = "Username",
|
||||
Password = Encoding.UTF8.GetBytes("Password"),
|
||||
ClientId = "ClientId",
|
||||
AuthenticationData = Encoding.UTF8.GetBytes("AuthenticationData"),
|
||||
AuthenticationMethod = "AuthenticationMethod",
|
||||
CleanSession = true,
|
||||
ReceiveMaximum = 123,
|
||||
WillFlag = true,
|
||||
WillTopic = "WillTopic",
|
||||
WillMessage = Encoding.UTF8.GetBytes("WillMessage"),
|
||||
WillRetain = true,
|
||||
KeepAlivePeriod = 456,
|
||||
MaximumPacketSize = 789,
|
||||
RequestProblemInformation = true,
|
||||
RequestResponseInformation = true,
|
||||
SessionExpiryInterval = 27,
|
||||
TopicAliasMaximum = 67,
|
||||
WillContentType = "WillContentType",
|
||||
WillCorrelationData = Encoding.UTF8.GetBytes("WillCorrelationData"),
|
||||
WillDelayInterval = 782,
|
||||
WillQoS = MqttQualityOfServiceLevel.ExactlyOnce,
|
||||
WillResponseTopic = "WillResponseTopic",
|
||||
WillMessageExpiryInterval = 542,
|
||||
WillPayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData,
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
},
|
||||
WillUserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("WillFoo", "WillBar")
|
||||
}
|
||||
};
|
||||
PacketIdentifier = 123,
|
||||
TopicFilters = ["TopicFilter1"],
|
||||
UserProperties = [new("Foo", "Bar")]
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connectPacket, MqttProtocolVersion.V311);
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubscribePacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(connectPacket.Username, deserialized.Username);
|
||||
CollectionAssert.AreEqual(connectPacket.Password, deserialized.Password);
|
||||
Assert.AreEqual(connectPacket.ClientId, deserialized.ClientId);
|
||||
CollectionAssert.AreEqual(null, deserialized.AuthenticationData); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.AuthenticationMethod); // Not supported in v3.1.1
|
||||
Assert.AreEqual(connectPacket.CleanSession, deserialized.CleanSession);
|
||||
Assert.AreEqual(0L, deserialized.ReceiveMaximum); // Not supported in v3.1.1
|
||||
Assert.AreEqual(connectPacket.WillFlag, deserialized.WillFlag);
|
||||
Assert.AreEqual(connectPacket.WillTopic, deserialized.WillTopic);
|
||||
CollectionAssert.AreEqual(connectPacket.WillMessage, deserialized.WillMessage);
|
||||
Assert.AreEqual(connectPacket.WillRetain, deserialized.WillRetain);
|
||||
Assert.AreEqual(connectPacket.KeepAlivePeriod, deserialized.KeepAlivePeriod);
|
||||
// MaximumPacketSize not available in MQTTv3.
|
||||
// RequestProblemInformation not available in MQTTv3.
|
||||
// RequestResponseInformation not available in MQTTv3.
|
||||
// SessionExpiryInterval not available in MQTTv3.
|
||||
// TopicAliasMaximum not available in MQTTv3.
|
||||
// WillContentType not available in MQTTv3.
|
||||
// WillCorrelationData not available in MQTTv3.
|
||||
// WillDelayInterval not available in MQTTv3.
|
||||
Assert.AreEqual(connectPacket.WillQoS, deserialized.WillQoS);
|
||||
// WillResponseTopic not available in MQTTv3.
|
||||
// WillMessageExpiryInterval not available in MQTTv3.
|
||||
// WillPayloadFormatIndicator not available in MQTTv3.
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
Assert.IsNull(deserialized.WillUserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttDisconnectPacket_V311()
|
||||
{
|
||||
var disconnectPacket = new MqttDisconnectPacket
|
||||
{
|
||||
ReasonCode = MqttDisconnectReasonCode.NormalDisconnection, // MQTTv3 has no other values than this.
|
||||
ReasonString = "ReasonString",
|
||||
ServerReference = "ServerReference",
|
||||
SessionExpiryInterval = 234,
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(disconnectPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(disconnectPacket.ReasonCode, deserialized.ReasonCode);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.ServerReference); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.SessionExpiryInterval); // Not supported in v3.1.1
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPingReqPacket_V311()
|
||||
{
|
||||
var pingReqPacket = new MqttPingReqPacket();
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pingReqPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.IsNotNull(deserialized);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPingRespPacket_V311()
|
||||
{
|
||||
var pingRespPacket = new MqttPingRespPacket();
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pingRespPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.IsNotNull(deserialized);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPubAckPacket_V311()
|
||||
{
|
||||
var pubAckPacket = new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCode = MqttPubAckReasonCode.NoMatchingSubscribers,
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubAckPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(pubAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(MqttPubAckReasonCode.Success, deserialized.ReasonCode); // Not supported in v3.1.1
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPubCompPacket_V311()
|
||||
{
|
||||
var pubCompPacket = new MqttPubCompPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCode = MqttPubCompReasonCode.PacketIdentifierNotFound,
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubCompPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(pubCompPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
// ReasonCode not available in MQTTv3.
|
||||
// ReasonString not available in MQTTv3.
|
||||
// UserProperties not available in MQTTv3.
|
||||
Assert.IsNull(deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPublishPacket_V311()
|
||||
{
|
||||
var publishPacket = new MqttPublishPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
Dup = true,
|
||||
Retain = true,
|
||||
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("Payload")),
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
|
||||
Topic = "Topic",
|
||||
ResponseTopic = "/Response",
|
||||
ContentType = "Content-Type",
|
||||
CorrelationData = Encoding.UTF8.GetBytes("CorrelationData"),
|
||||
TopicAlias = 27,
|
||||
SubscriptionIdentifiers = new List<uint>
|
||||
{
|
||||
123
|
||||
},
|
||||
MessageExpiryInterval = 38,
|
||||
PayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData,
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(publishPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
|
||||
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
|
||||
CollectionAssert.AreEqual(publishPacket.Payload.ToArray(), deserialized.Payload.ToArray());
|
||||
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
|
||||
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
|
||||
Assert.AreEqual(null, deserialized.ResponseTopic); // Not supported in v3.1.1.
|
||||
Assert.AreEqual(null, deserialized.ContentType); // Not supported in v3.1.1.
|
||||
CollectionAssert.AreEqual(null, deserialized.CorrelationData); // Not supported in v3.1.1.
|
||||
Assert.AreEqual(0U, deserialized.TopicAlias); // Not supported in v3.1.1.
|
||||
CollectionAssert.AreEqual(null, deserialized.SubscriptionIdentifiers); // Not supported in v3.1.1
|
||||
Assert.AreEqual(0U, deserialized.MessageExpiryInterval); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttPayloadFormatIndicator.Unspecified, deserialized.PayloadFormatIndicator); // Not supported in v3.1.1
|
||||
Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPubRecPacket_V311()
|
||||
{
|
||||
var pubRecPacket = new MqttPubRecPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCode = MqttPubRecReasonCode.UnspecifiedError,
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRecPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(pubRecPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
// ReasonCode not available in MQTTv3.
|
||||
// ReasonString not available in MQTTv3.
|
||||
// UserProperties not available in MQTTv3.
|
||||
Assert.IsNull(deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttPubRelPacket_V311()
|
||||
{
|
||||
var pubRelPacket = new MqttPubRelPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound,
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRelPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(pubRelPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
// ReasonCode not available in MQTTv3.
|
||||
// ReasonString not available in MQTTv3.
|
||||
// UserProperties not available in MQTTv3.
|
||||
Assert.IsNull(deserialized.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttSubAckPacket_V311()
|
||||
{
|
||||
var subAckPacket = new MqttSubAckPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonString = "ReasonString",
|
||||
ReasonCodes = new List<MqttSubscribeReasonCode>
|
||||
{
|
||||
MqttSubscribeReasonCode.GrantedQoS1
|
||||
},
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subAckPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(subAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
Assert.AreEqual(subAckPacket.ReasonCodes.Count, deserialized.ReasonCodes.Count);
|
||||
Assert.AreEqual(subAckPacket.ReasonCodes[0], deserialized.ReasonCodes[0]);
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttSubscribePacket_V311()
|
||||
{
|
||||
var subscribePacket = new MqttSubscribePacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
SubscriptionIdentifier = 456,
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter
|
||||
{
|
||||
Topic = "Topic",
|
||||
NoLocal = true,
|
||||
RetainHandling = MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly,
|
||||
RetainAsPublished = true,
|
||||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
|
||||
}
|
||||
},
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subscribePacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(subscribePacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(0U, deserialized.SubscriptionIdentifier); // Not supported in v3.1.1
|
||||
Assert.AreEqual(1, deserialized.TopicFilters.Count);
|
||||
Assert.AreEqual(subscribePacket.TopicFilters[0].Topic, deserialized.TopicFilters[0].Topic);
|
||||
Assert.AreEqual(false, deserialized.TopicFilters[0].NoLocal); // Not supported in v3.1.1
|
||||
Assert.AreEqual(MqttRetainHandling.SendAtSubscribe, deserialized.TopicFilters[0].RetainHandling); // Not supported in v3.1.1
|
||||
Assert.AreEqual(false, deserialized.TopicFilters[0].RetainAsPublished); // Not supported in v3.1.1
|
||||
Assert.AreEqual(subscribePacket.TopicFilters[0].QualityOfServiceLevel, deserialized.TopicFilters[0].QualityOfServiceLevel);
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttUnsubAckPacket_V311()
|
||||
{
|
||||
var unsubAckPacket = new MqttUnsubAckPacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
ReasonCodes = new List<MqttUnsubscribeReasonCode>
|
||||
{
|
||||
MqttUnsubscribeReasonCode.ImplementationSpecificError
|
||||
},
|
||||
ReasonString = "ReasonString",
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubAckPacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(unsubAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(null, deserialized.ReasonString); // Not supported in v3.1.1
|
||||
CollectionAssert.AreEqual(null, deserialized.ReasonCodes); // Not supported in v3.1.1
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties); // Not supported in v3.1.1
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Serialize_Full_MqttUnsubscribePacket_V311()
|
||||
{
|
||||
var unsubscribePacket = new MqttUnsubscribePacket
|
||||
{
|
||||
PacketIdentifier = 123,
|
||||
TopicFilters = new List<string>
|
||||
{
|
||||
"TopicFilter1"
|
||||
},
|
||||
UserProperties = new List<MqttUserProperty>
|
||||
{
|
||||
new MqttUserProperty("Foo", "Bar")
|
||||
}
|
||||
};
|
||||
|
||||
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubscribePacket, MqttProtocolVersion.V311);
|
||||
|
||||
Assert.AreEqual(unsubscribePacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(unsubscribePacket.TopicFilters.Count, deserialized.TopicFilters.Count);
|
||||
Assert.AreEqual(unsubscribePacket.TopicFilters[0], deserialized.TopicFilters[0]);
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties);
|
||||
}
|
||||
Assert.AreEqual(unsubscribePacket.PacketIdentifier, deserialized.PacketIdentifier);
|
||||
Assert.AreEqual(unsubscribePacket.TopicFilters.Count, deserialized.TopicFilters.Count);
|
||||
Assert.AreEqual(unsubscribePacket.TopicFilters[0], deserialized.TopicFilters[0]);
|
||||
CollectionAssert.AreEqual(null, deserialized.UserProperties);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttPacketSerialization_V5_Tests
|
||||
{
|
||||
|
||||
@@ -1,102 +1,101 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Formatter
|
||||
namespace MQTTnet.Tests.Formatter;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttPacketWriter_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketWriter_Tests
|
||||
[TestMethod]
|
||||
public void Reset_After_Usage()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Reset_After_Usage()
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("AString");
|
||||
writer.WriteByte(0x1);
|
||||
writer.WriteByte(0x0);
|
||||
writer.WriteByte(0x1);
|
||||
writer.WriteVariableByteInteger(1234U);
|
||||
writer.WriteVariableByteInteger(9876U);
|
||||
|
||||
writer.Reset(0);
|
||||
|
||||
Assert.AreEqual(0, writer.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Use_All_Data_Types()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("AString");
|
||||
writer.WriteByte(0x1);
|
||||
writer.WriteByte(0x0);
|
||||
writer.WriteByte(0x1);
|
||||
writer.WriteVariableByteInteger(1234U);
|
||||
writer.WriteVariableByteInteger(9876U);
|
||||
|
||||
var buffer = writer.GetBuffer();
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(buffer, 0, writer.Length);
|
||||
|
||||
Assert.AreEqual("AString", reader.ReadString());
|
||||
Assert.IsTrue(reader.ReadByte() == 1);
|
||||
Assert.IsTrue(reader.ReadByte() == 0);
|
||||
Assert.IsTrue(reader.ReadByte() == 1);
|
||||
Assert.AreEqual(1234U, reader.ReadVariableByteInteger());
|
||||
Assert.AreEqual(9876U, reader.ReadVariableByteInteger());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Throw_If_String_Too_Long()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
|
||||
writer.WriteString(string.Empty.PadLeft(65536));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Write_And_Read_Multiple_Times()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteBinary(new byte[1234]);
|
||||
writer.WriteByte(0x01);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574489);
|
||||
writer.WriteBinary(new byte[48]);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinary(new byte[3]);
|
||||
|
||||
var readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(readPayload, 0, readPayload.Length);
|
||||
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("AString");
|
||||
writer.WriteByte(0x1);
|
||||
writer.WriteByte(0x0);
|
||||
writer.WriteByte(0x1);
|
||||
writer.WriteVariableByteInteger(1234U);
|
||||
writer.WriteVariableByteInteger(9876U);
|
||||
reader.Seek(0);
|
||||
|
||||
writer.Reset(0);
|
||||
|
||||
Assert.AreEqual(0, writer.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Use_All_Data_Types()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("AString");
|
||||
writer.WriteByte(0x1);
|
||||
writer.WriteByte(0x0);
|
||||
writer.WriteByte(0x1);
|
||||
writer.WriteVariableByteInteger(1234U);
|
||||
writer.WriteVariableByteInteger(9876U);
|
||||
|
||||
var buffer = writer.GetBuffer();
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(buffer, 0, writer.Length);
|
||||
|
||||
Assert.AreEqual("AString", reader.ReadString());
|
||||
Assert.IsTrue(reader.ReadByte() == 1);
|
||||
Assert.IsTrue(reader.ReadByte() == 0);
|
||||
Assert.IsTrue(reader.ReadByte() == 1);
|
||||
Assert.AreEqual(1234U, reader.ReadVariableByteInteger());
|
||||
Assert.AreEqual(9876U, reader.ReadVariableByteInteger());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Throw_If_String_Too_Long()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
|
||||
writer.WriteString(string.Empty.PadLeft(65536));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Write_And_Read_Multiple_Times()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteBinary(new byte[1234]);
|
||||
writer.WriteByte(0x01);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574489);
|
||||
writer.WriteBinary(new byte[48]);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinary(new byte[3]);
|
||||
|
||||
var readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(readPayload, 0, readPayload.Length);
|
||||
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
reader.Seek(0);
|
||||
|
||||
reader.ReadString();
|
||||
reader.ReadBinaryData();
|
||||
reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
reader.ReadVariableByteInteger();
|
||||
reader.ReadString();
|
||||
reader.ReadVariableByteInteger();
|
||||
reader.ReadBinaryData();
|
||||
reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
reader.ReadString();
|
||||
reader.ReadBinaryData();
|
||||
}
|
||||
reader.ReadString();
|
||||
reader.ReadBinaryData();
|
||||
reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
reader.ReadVariableByteInteger();
|
||||
reader.ReadString();
|
||||
reader.ReadVariableByteInteger();
|
||||
reader.ReadBinaryData();
|
||||
reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
reader.ReadString();
|
||||
reader.ReadBinaryData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,14 @@
|
||||
using System;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Helpers
|
||||
{
|
||||
public static class MqttClientExtensions
|
||||
{
|
||||
public static TestApplicationMessageReceivedHandler TrackReceivedMessages(this IMqttClient client)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(client);
|
||||
namespace MQTTnet.Tests.Helpers;
|
||||
|
||||
return new TestApplicationMessageReceivedHandler(client);
|
||||
}
|
||||
public static class MqttClientExtensions
|
||||
{
|
||||
public static TestApplicationMessageReceivedHandler TrackReceivedMessages(this IMqttClient client)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(client);
|
||||
|
||||
return new TestApplicationMessageReceivedHandler(client);
|
||||
}
|
||||
}
|
||||
@@ -5,16 +5,15 @@
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Helpers
|
||||
namespace MQTTnet.Tests.Helpers;
|
||||
|
||||
public static class MqttPacketWriterExtensions
|
||||
{
|
||||
public static class MqttPacketWriterExtensions
|
||||
public static byte[] AddMqttHeader(this MqttBufferWriter writer, MqttControlPacketType header, byte[] body)
|
||||
{
|
||||
public static byte[] AddMqttHeader(this MqttBufferWriter writer, MqttControlPacketType header, byte[] body)
|
||||
{
|
||||
writer.WriteByte(MqttBufferWriter.BuildFixedHeader(header));
|
||||
writer.WriteVariableByteInteger((uint)body.Length);
|
||||
writer.WriteBinary(body, 0, body.Length);
|
||||
return writer.GetBuffer();
|
||||
}
|
||||
writer.WriteByte(MqttBufferWriter.BuildFixedHeader(header));
|
||||
writer.WriteVariableByteInteger((uint)body.Length);
|
||||
writer.WriteBinary(body, 0, body.Length);
|
||||
return writer.GetBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,21 +5,20 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MQTTnet.Tests.Helpers
|
||||
namespace MQTTnet.Tests.Helpers;
|
||||
|
||||
public static class ReflectionExtensions
|
||||
{
|
||||
public static class ReflectionExtensions
|
||||
public static object GetFieldValue(this object source, string fieldName)
|
||||
{
|
||||
public static object GetFieldValue(this object source, string fieldName)
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
|
||||
var field = source.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (field == null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
|
||||
var field = source.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (field == null)
|
||||
{
|
||||
throw new ArgumentException($"Field {fieldName} not found.");
|
||||
}
|
||||
|
||||
return field.GetValue(source);
|
||||
throw new ArgumentException($"Field {fieldName} not found.");
|
||||
}
|
||||
|
||||
return field.GetValue(source);
|
||||
}
|
||||
}
|
||||
@@ -8,133 +8,133 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
namespace MQTTnet.Tests.Internal;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class AsyncEvent_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class AsyncEvent_Tests
|
||||
int _testEventAsyncCount;
|
||||
|
||||
[TestMethod]
|
||||
public async Task Attach_and_Detach_Sync_And_Async_EventHandler()
|
||||
{
|
||||
int _testEventAsyncCount;
|
||||
var testClass = new TestClass();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Attach_and_Detach_Sync_And_Async_EventHandler()
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
testClass.TestEvent += OnTestEvent;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
Assert.AreEqual(2, _testEventAsyncCount);
|
||||
|
||||
testClass.TestEvent -= OnTestEvent;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
Assert.AreEqual(3, _testEventAsyncCount);
|
||||
|
||||
testClass.TestEventAsync -= OnTestEventAsync;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
Assert.AreEqual(3, _testEventAsyncCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Attach_EventHandler()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
|
||||
Assert.AreEqual(1, _testEventAsyncCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Attach_Sync_And_Async_EventHandler()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
testClass.TestEvent += OnTestEvent;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
|
||||
Assert.AreEqual(2, _testEventAsyncCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Detach_EventHandler()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
testClass.TestEventAsync -= OnTestEventAsync;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
|
||||
Assert.AreEqual(0, _testEventAsyncCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Has_Handlers()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
|
||||
Assert.AreEqual(true, testClass.HasTestHandlers);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void No_Handlers()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
Assert.AreEqual(false, testClass.HasTestHandlers);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Remove_Handlers()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
testClass.TestEventAsync -= OnTestEventAsync;
|
||||
|
||||
Assert.AreEqual(false, testClass.HasTestHandlers);
|
||||
}
|
||||
|
||||
void OnTestEvent(EventArgs arg)
|
||||
{
|
||||
Interlocked.Increment(ref _testEventAsyncCount);
|
||||
}
|
||||
|
||||
Task OnTestEventAsync(EventArgs arg)
|
||||
{
|
||||
Interlocked.Increment(ref _testEventAsyncCount);
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
sealed class TestClass
|
||||
{
|
||||
readonly AsyncEvent<EventArgs> _testEvent = new();
|
||||
|
||||
public event Action<EventArgs> TestEvent
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
testClass.TestEvent += OnTestEvent;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
Assert.AreEqual(2, _testEventAsyncCount);
|
||||
|
||||
testClass.TestEvent -= OnTestEvent;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
Assert.AreEqual(3, _testEventAsyncCount);
|
||||
|
||||
testClass.TestEventAsync -= OnTestEventAsync;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
Assert.AreEqual(3, _testEventAsyncCount);
|
||||
add => _testEvent.AddHandler(value);
|
||||
remove => _testEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Attach_EventHandler()
|
||||
public event Func<EventArgs, Task> TestEventAsync
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
|
||||
Assert.AreEqual(1, _testEventAsyncCount);
|
||||
add => _testEvent.AddHandler(value);
|
||||
remove => _testEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Attach_Sync_And_Async_EventHandler()
|
||||
public bool HasTestHandlers => _testEvent.HasHandlers;
|
||||
|
||||
public Task FireEventAsync(EventArgs eventArgs)
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
testClass.TestEvent += OnTestEvent;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
|
||||
Assert.AreEqual(2, _testEventAsyncCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Detach_EventHandler()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
testClass.TestEventAsync -= OnTestEventAsync;
|
||||
|
||||
await testClass.FireEventAsync(EventArgs.Empty);
|
||||
|
||||
Assert.AreEqual(0, _testEventAsyncCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Has_Handlers()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
|
||||
Assert.AreEqual(true, testClass.HasTestHandlers);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void No_Handlers()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
|
||||
Assert.AreEqual(false, testClass.HasTestHandlers);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Remove_Handlers()
|
||||
{
|
||||
var testClass = new TestClass();
|
||||
testClass.TestEventAsync += OnTestEventAsync;
|
||||
testClass.TestEventAsync -= OnTestEventAsync;
|
||||
|
||||
Assert.AreEqual(false, testClass.HasTestHandlers);
|
||||
}
|
||||
|
||||
void OnTestEvent(EventArgs arg)
|
||||
{
|
||||
Interlocked.Increment(ref _testEventAsyncCount);
|
||||
}
|
||||
|
||||
Task OnTestEventAsync(EventArgs arg)
|
||||
{
|
||||
Interlocked.Increment(ref _testEventAsyncCount);
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
sealed class TestClass
|
||||
{
|
||||
readonly AsyncEvent<EventArgs> _testEvent = new AsyncEvent<EventArgs>();
|
||||
|
||||
public event Action<EventArgs> TestEvent
|
||||
{
|
||||
add => _testEvent.AddHandler(value);
|
||||
remove => _testEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public event Func<EventArgs, Task> TestEventAsync
|
||||
{
|
||||
add => _testEvent.AddHandler(value);
|
||||
remove => _testEvent.RemoveHandler(value);
|
||||
}
|
||||
|
||||
public bool HasTestHandlers => _testEvent.HasHandlers;
|
||||
|
||||
public Task FireEventAsync(EventArgs eventArgs)
|
||||
{
|
||||
return _testEvent.InvokeAsync(eventArgs);
|
||||
}
|
||||
return _testEvent.InvokeAsync(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,283 +9,267 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
namespace MQTTnet.Tests.Internal;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class AsyncLock_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class AsyncLock_Tests
|
||||
[TestMethod]
|
||||
public async Task Cancellation_Of_Awaiter()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Cancellation_Of_Awaiter()
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
// This call will not yet "release" the lock due to missing _using_.
|
||||
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
var counter = 0;
|
||||
|
||||
Debug.WriteLine("Prepared locked lock.");
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
// SHOULD GET TIMEOUT!
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1));
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 1 incremented");
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
// SHOULD GET TIMEOUT!
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2));
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 2 incremented");
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
// SHOULD GET TIMEOUT!
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3));
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 3 incremented");
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(4));
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 4 incremented");
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 5 incremented");
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(6));
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 6 incremented");
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
||||
Debug.WriteLine("Delay before release...");
|
||||
await Task.Delay(TimeSpan.FromSeconds(3.1));
|
||||
releaser.Dispose();
|
||||
|
||||
Debug.WriteLine("Wait for all tasks...");
|
||||
await Task.Delay(TimeSpan.FromSeconds(6.1));
|
||||
|
||||
Assert.AreEqual(3, counter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Lock_Parallel_Tasks()
|
||||
{
|
||||
const int taskCount = 50;
|
||||
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
var tasks = new Task[taskCount];
|
||||
var globalI = 0;
|
||||
for (var i = 0; i < taskCount; i++)
|
||||
{
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
// This call will not yet "release" the lock due to missing _using_.
|
||||
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
var counter = 0;
|
||||
|
||||
Debug.WriteLine("Prepared locked lock.");
|
||||
|
||||
_ = Task.Run(
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
tasks[i] = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
// SHOULD GET TIMEOUT!
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
|
||||
using (await @lock.EnterAsync())
|
||||
{
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 1 incremented");
|
||||
counter++;
|
||||
}
|
||||
var localI = globalI;
|
||||
await Task.Delay(5); // Increase the chance for wrong data.
|
||||
localI++;
|
||||
globalI = localI;
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
// SHOULD GET TIMEOUT!
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
|
||||
{
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 2 incremented");
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
// SHOULD GET TIMEOUT!
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 3 incremented");
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(4)))
|
||||
{
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 4 incremented");
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 5 incremented");
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(6)))
|
||||
{
|
||||
using (await @lock.EnterAsync(timeout.Token))
|
||||
{
|
||||
Debug.WriteLine("Task 6 incremented");
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Debug.WriteLine("Delay before release...");
|
||||
await Task.Delay(TimeSpan.FromSeconds(3.1));
|
||||
releaser.Dispose();
|
||||
|
||||
Debug.WriteLine("Wait for all tasks...");
|
||||
await Task.Delay(TimeSpan.FromSeconds(6.1));
|
||||
|
||||
Assert.AreEqual(3, counter);
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Lock_Parallel_Tasks()
|
||||
Task.WaitAll(tasks);
|
||||
Assert.AreEqual(taskCount, globalI);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Lock_10_Parallel_Tasks_With_Dispose_Doesnt_Lockup()
|
||||
{
|
||||
const int ThreadsCount = 10;
|
||||
|
||||
var threads = new Task[ThreadsCount];
|
||||
var @lock = new AsyncLock();
|
||||
var globalI = 0;
|
||||
for (var i = 0; i < ThreadsCount; i++)
|
||||
{
|
||||
const int taskCount = 50;
|
||||
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
var tasks = new Task[taskCount];
|
||||
var globalI = 0;
|
||||
for (var i = 0; i < taskCount; i++)
|
||||
{
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
tasks[i] = Task.Run(
|
||||
threads[i] = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (await @lock.EnterAsync())
|
||||
{
|
||||
var localI = globalI;
|
||||
await Task.Delay(5); // Increase the chance for wrong data.
|
||||
await Task.Delay(10); // Increase the chance for wrong data.
|
||||
localI++;
|
||||
globalI = localI;
|
||||
}
|
||||
})
|
||||
.ContinueWith(
|
||||
x =>
|
||||
{
|
||||
if (globalI == 5)
|
||||
{
|
||||
@lock.Dispose();
|
||||
@lock = new AsyncLock();
|
||||
}
|
||||
|
||||
if (x.Exception != null)
|
||||
{
|
||||
Debug.WriteLine(x.Exception.GetBaseException().GetType().Name);
|
||||
}
|
||||
});
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
Assert.AreEqual(taskCount, globalI);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Lock_10_Parallel_Tasks_With_Dispose_Doesnt_Lockup()
|
||||
Task.WaitAll(threads);
|
||||
|
||||
// Expect only 6 because the others are failing due to disposal (if (globalI == 5)).
|
||||
Assert.AreEqual(6, globalI);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Lock_Serial_Calls()
|
||||
{
|
||||
var sum = 0;
|
||||
|
||||
var @lock = new AsyncLock();
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
const int ThreadsCount = 10;
|
||||
|
||||
var threads = new Task[ThreadsCount];
|
||||
var @lock = new AsyncLock();
|
||||
var globalI = 0;
|
||||
for (var i = 0; i < ThreadsCount; i++)
|
||||
{
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
threads[i] = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (await @lock.EnterAsync())
|
||||
{
|
||||
var localI = globalI;
|
||||
await Task.Delay(10); // Increase the chance for wrong data.
|
||||
localI++;
|
||||
globalI = localI;
|
||||
}
|
||||
})
|
||||
.ContinueWith(
|
||||
x =>
|
||||
{
|
||||
if (globalI == 5)
|
||||
{
|
||||
@lock.Dispose();
|
||||
@lock = new AsyncLock();
|
||||
}
|
||||
|
||||
if (x.Exception != null)
|
||||
{
|
||||
Debug.WriteLine(x.Exception.GetBaseException().GetType().Name);
|
||||
}
|
||||
});
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
|
||||
Task.WaitAll(threads);
|
||||
|
||||
// Expect only 6 because the others are failing due to disposal (if (globalI == 5)).
|
||||
Assert.AreEqual(6, globalI);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Lock_Serial_Calls()
|
||||
{
|
||||
var sum = 0;
|
||||
|
||||
var @lock = new AsyncLock();
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(100, sum);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Test_Cancellation()
|
||||
{
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
// This call will never "release" the lock due to missing _using_.
|
||||
await @lock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Test_Cancellation_With_Later_Access()
|
||||
{
|
||||
var asyncLock = new AsyncLock();
|
||||
|
||||
var releaser = await asyncLock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
await asyncLock.EnterAsync(timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Assert.Fail("Exception should be thrown!");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
releaser.Dispose();
|
||||
|
||||
using (await asyncLock.EnterAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
// When the method finished, the thread got access.
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_After_Cancellation()
|
||||
{
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
// This call will not yet "release" the lock due to missing _using_.
|
||||
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Expected exception!
|
||||
}
|
||||
|
||||
releaser.Dispose();
|
||||
|
||||
// Regular usage after cancellation.
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(100, sum);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Test_Cancellation()
|
||||
{
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
// This call will never "release" the lock due to missing _using_.
|
||||
await @lock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
|
||||
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Test_Cancellation_With_Later_Access()
|
||||
{
|
||||
var asyncLock = new AsyncLock();
|
||||
|
||||
var releaser = await asyncLock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
await asyncLock.EnterAsync(timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
Assert.Fail("Exception should be thrown!");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
releaser.Dispose();
|
||||
|
||||
using (await asyncLock.EnterAsync(CancellationToken.None).ConfigureAwait(false))
|
||||
{
|
||||
// When the method finished, the thread got access.
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_After_Cancellation()
|
||||
{
|
||||
var @lock = new AsyncLock();
|
||||
|
||||
// This call will not yet "release" the lock due to missing _using_.
|
||||
var releaser = await @lock.EnterAsync().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
|
||||
await @lock.EnterAsync(cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Expected exception!
|
||||
}
|
||||
|
||||
releaser.Dispose();
|
||||
|
||||
// Regular usage after cancellation.
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
|
||||
using (await @lock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,138 +8,138 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
namespace MQTTnet.Tests.Internal;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class AsyncQueue_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class AsyncQueue_Tests
|
||||
[TestMethod]
|
||||
public async Task Preserve_Order()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Preserve_Order()
|
||||
{
|
||||
var queue = new AsyncQueue<string>();
|
||||
queue.Enqueue("1");
|
||||
queue.Enqueue("2");
|
||||
queue.Enqueue("3");
|
||||
var queue = new AsyncQueue<string>();
|
||||
queue.Enqueue("1");
|
||||
queue.Enqueue("2");
|
||||
queue.Enqueue("3");
|
||||
|
||||
Assert.AreEqual("1", (await queue.TryDequeueAsync(CancellationToken.None)).Item);
|
||||
Assert.AreEqual("2", (await queue.TryDequeueAsync(CancellationToken.None)).Item);
|
||||
Assert.AreEqual("3", (await queue.TryDequeueAsync(CancellationToken.None)).Item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Count()
|
||||
{
|
||||
var queue = new AsyncQueue<string>();
|
||||
|
||||
queue.Enqueue("1");
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
|
||||
queue.Enqueue("2");
|
||||
Assert.AreEqual(2, queue.Count);
|
||||
|
||||
queue.Enqueue("3");
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Cancellation()
|
||||
{
|
||||
var queue = new AsyncQueue<int>();
|
||||
|
||||
bool success;
|
||||
using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
success = (await queue.TryDequeueAsync(cancellationTokenSource.Token)).IsSuccess;
|
||||
}
|
||||
|
||||
Assert.AreEqual(false, success);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Process_Async()
|
||||
{
|
||||
var queue = new AsyncQueue<int>();
|
||||
|
||||
var sum = 0;
|
||||
var worker = Task.Run(async () =>
|
||||
{
|
||||
while (sum < 6)
|
||||
{
|
||||
sum += (await queue.TryDequeueAsync(CancellationToken.None)).Item;
|
||||
}
|
||||
});
|
||||
|
||||
queue.Enqueue(1);
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(2);
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(3);
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(6, sum);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, worker.Status);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Process_Async_With_Initial_Delay()
|
||||
{
|
||||
var queue = new AsyncQueue<int>();
|
||||
|
||||
var sum = 0;
|
||||
var worker = Task.Run(async () =>
|
||||
{
|
||||
while (sum < 6)
|
||||
{
|
||||
sum += (await queue.TryDequeueAsync(CancellationToken.None)).Item;
|
||||
}
|
||||
});
|
||||
|
||||
// This line is the diff to test _Process_Async_
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(1);
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(2);
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(3);
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(6, sum);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, worker.Status);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Dequeue_Sync()
|
||||
{
|
||||
var queue = new AsyncQueue<string>();
|
||||
queue.Enqueue("1");
|
||||
queue.Enqueue("2");
|
||||
queue.Enqueue("3");
|
||||
|
||||
Assert.AreEqual("1", queue.TryDequeue().Item);
|
||||
Assert.AreEqual("2", queue.TryDequeue().Item);
|
||||
Assert.AreEqual("3", queue.TryDequeue().Item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Clear()
|
||||
{
|
||||
var queue = new AsyncQueue<string>();
|
||||
queue.Enqueue("1");
|
||||
queue.Enqueue("2");
|
||||
queue.Enqueue("3");
|
||||
|
||||
queue.Clear();
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
|
||||
queue.Enqueue("4");
|
||||
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
Assert.AreEqual("4", queue.TryDequeue().Item);
|
||||
}
|
||||
Assert.AreEqual("1", (await queue.TryDequeueAsync(CancellationToken.None)).Item);
|
||||
Assert.AreEqual("2", (await queue.TryDequeueAsync(CancellationToken.None)).Item);
|
||||
Assert.AreEqual("3", (await queue.TryDequeueAsync(CancellationToken.None)).Item);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Count()
|
||||
{
|
||||
var queue = new AsyncQueue<string>();
|
||||
|
||||
queue.Enqueue("1");
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
|
||||
queue.Enqueue("2");
|
||||
Assert.AreEqual(2, queue.Count);
|
||||
|
||||
queue.Enqueue("3");
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Cancellation()
|
||||
{
|
||||
var queue = new AsyncQueue<int>();
|
||||
|
||||
bool success;
|
||||
using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
{
|
||||
success = (await queue.TryDequeueAsync(cancellationTokenSource.Token)).IsSuccess;
|
||||
}
|
||||
|
||||
Assert.AreEqual(false, success);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Process_Async()
|
||||
{
|
||||
var queue = new AsyncQueue<int>();
|
||||
|
||||
var sum = 0;
|
||||
var worker = Task.Run(async () =>
|
||||
{
|
||||
while (sum < 6)
|
||||
{
|
||||
sum += (await queue.TryDequeueAsync(CancellationToken.None)).Item;
|
||||
}
|
||||
});
|
||||
|
||||
queue.Enqueue(1);
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(2);
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(3);
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(6, sum);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, worker.Status);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Process_Async_With_Initial_Delay()
|
||||
{
|
||||
var queue = new AsyncQueue<int>();
|
||||
|
||||
var sum = 0;
|
||||
var worker = Task.Run(async () =>
|
||||
{
|
||||
while (sum < 6)
|
||||
{
|
||||
sum += (await queue.TryDequeueAsync(CancellationToken.None)).Item;
|
||||
}
|
||||
});
|
||||
|
||||
// This line is the diff to test _Process_Async_
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(1);
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(2);
|
||||
await Task.Delay(500);
|
||||
|
||||
queue.Enqueue(3);
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(6, sum);
|
||||
Assert.AreEqual(TaskStatus.RanToCompletion, worker.Status);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Dequeue_Sync()
|
||||
{
|
||||
var queue = new AsyncQueue<string>();
|
||||
queue.Enqueue("1");
|
||||
queue.Enqueue("2");
|
||||
queue.Enqueue("3");
|
||||
|
||||
Assert.AreEqual("1", queue.TryDequeue().Item);
|
||||
Assert.AreEqual("2", queue.TryDequeue().Item);
|
||||
Assert.AreEqual("3", queue.TryDequeue().Item);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Clear()
|
||||
{
|
||||
var queue = new AsyncQueue<string>();
|
||||
queue.Enqueue("1");
|
||||
queue.Enqueue("2");
|
||||
queue.Enqueue("3");
|
||||
|
||||
queue.Clear();
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
|
||||
queue.Enqueue("4");
|
||||
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
Assert.AreEqual("4", queue.TryDequeue().Item);
|
||||
}
|
||||
}
|
||||
@@ -9,120 +9,114 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
namespace MQTTnet.Tests.Internal;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class AsyncSignal_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class AsyncSignal_Tests
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Cancel_If_No_Signal()
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Cancel_If_No_Signal()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2)))
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(2));
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
|
||||
Assert.Fail("There is no signal. So we must fail here!");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ObjectDisposedException))]
|
||||
public async Task Dispose_Properly()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// The timeout will not be reached but another task will kill the async signal.
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(999));
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
await Task.Delay(2000, CancellationToken.None);
|
||||
asyncSignal.Dispose();
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
Assert.Fail("There is no signal. So we must fail here!");
|
||||
}
|
||||
}
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ObjectDisposedException))]
|
||||
public async Task Dispose_Properly()
|
||||
Assert.Fail("There is no signal. So we must fail here!");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Reset_Signal()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// WaitAsync should fail because no signal is available.
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// The timeout will not be reached but another task will kill the async signal.
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(999)))
|
||||
try
|
||||
{
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await Task.Delay(2000, CancellationToken.None);
|
||||
asyncSignal.Dispose();
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
|
||||
Assert.Fail("There is no signal. So we must fail here!");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Reset_Signal()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// WaitAsync should fail because no signal is available.
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
try
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100)))
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100)))
|
||||
{
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
}
|
||||
|
||||
Assert.Fail("This must fail because the signal is not yet set.");
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
|
||||
asyncSignal.Set();
|
||||
|
||||
// WaitAsync should return directly because the signal is available.
|
||||
await asyncSignal.WaitAsync();
|
||||
|
||||
Assert.Fail("This must fail because the signal is not yet set.");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Signal()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// The timeout will not be reached but another task will kill the async signal.
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await Task.Delay(1000, CancellationToken.None);
|
||||
asyncSignal.Set();
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
Assert.IsTrue(stopwatch.ElapsedMilliseconds > 900);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public async Task Fail_For_Two_Waiters()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
asyncSignal.Set();
|
||||
|
||||
// This thread will wait properly because it is the first waiter.
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await asyncSignal.WaitAsync();
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Now the current thread must fail because there is already a waiter.
|
||||
// WaitAsync should return directly because the signal is available.
|
||||
await asyncSignal.WaitAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Signal()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// The timeout will not be reached but another task will kill the async signal.
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3));
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await Task.Delay(1000, CancellationToken.None);
|
||||
asyncSignal.Set();
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await asyncSignal.WaitAsync(timeout.Token);
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
Assert.IsTrue(stopwatch.ElapsedMilliseconds > 900);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public async Task Fail_For_Two_Waiters()
|
||||
{
|
||||
var asyncSignal = new AsyncSignal();
|
||||
|
||||
// This thread will wait properly because it is the first waiter.
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
await asyncSignal.WaitAsync().ConfigureAwait(false);
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Now the current thread must fail because there is already a waiter.
|
||||
await asyncSignal.WaitAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -8,122 +8,122 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
namespace MQTTnet.Tests.Internal;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class BlockingQueue_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class BlockingQueue_Tests
|
||||
[TestMethod]
|
||||
public void Preserve_Order()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Preserve_Order()
|
||||
{
|
||||
var queue = new BlockingQueue<string>();
|
||||
queue.Enqueue("a");
|
||||
queue.Enqueue("b");
|
||||
queue.Enqueue("c");
|
||||
var queue = new BlockingQueue<string>();
|
||||
queue.Enqueue("a");
|
||||
queue.Enqueue("b");
|
||||
queue.Enqueue("c");
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
Assert.AreEqual("a", queue.Dequeue());
|
||||
Assert.AreEqual("b", queue.Dequeue());
|
||||
Assert.AreEqual("c", queue.Dequeue());
|
||||
}
|
||||
Assert.AreEqual("a", queue.Dequeue());
|
||||
Assert.AreEqual("b", queue.Dequeue());
|
||||
Assert.AreEqual("c", queue.Dequeue());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Remove_First_Items()
|
||||
{
|
||||
var queue = new BlockingQueue<string>();
|
||||
queue.Enqueue("a");
|
||||
queue.Enqueue("b");
|
||||
queue.Enqueue("c");
|
||||
[TestMethod]
|
||||
public void Remove_First_Items()
|
||||
{
|
||||
var queue = new BlockingQueue<string>();
|
||||
queue.Enqueue("a");
|
||||
queue.Enqueue("b");
|
||||
queue.Enqueue("c");
|
||||
|
||||
Assert.AreEqual("a", queue.RemoveFirst());
|
||||
Assert.AreEqual("b", queue.RemoveFirst());
|
||||
Assert.AreEqual("a", queue.RemoveFirst());
|
||||
Assert.AreEqual("b", queue.RemoveFirst());
|
||||
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
|
||||
Assert.AreEqual("c", queue.Dequeue());
|
||||
}
|
||||
Assert.AreEqual("c", queue.Dequeue());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Clear_Items()
|
||||
{
|
||||
var queue = new BlockingQueue<string>();
|
||||
queue.Enqueue("a");
|
||||
queue.Enqueue("b");
|
||||
queue.Enqueue("c");
|
||||
[TestMethod]
|
||||
public void Clear_Items()
|
||||
{
|
||||
var queue = new BlockingQueue<string>();
|
||||
queue.Enqueue("a");
|
||||
queue.Enqueue("b");
|
||||
queue.Enqueue("c");
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
queue.Clear();
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Wait_For_Item()
|
||||
{
|
||||
var queue = new BlockingQueue<string>();
|
||||
[TestMethod]
|
||||
public async Task Wait_For_Item()
|
||||
{
|
||||
var queue = new BlockingQueue<string>();
|
||||
|
||||
string item = null;
|
||||
string item = null;
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Run(() =>
|
||||
{
|
||||
item = queue.Dequeue();
|
||||
});
|
||||
Task.Run(() =>
|
||||
{
|
||||
item = queue.Dequeue();
|
||||
});
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
await Task.Delay(100);
|
||||
await Task.Delay(100);
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
Assert.AreEqual(null, item);
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
Assert.AreEqual(null, item);
|
||||
|
||||
queue.Enqueue("x");
|
||||
queue.Enqueue("x");
|
||||
|
||||
await Task.Delay(100);
|
||||
await Task.Delay(100);
|
||||
|
||||
Assert.AreEqual("x", item);
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
Assert.AreEqual("x", item);
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Wait_For_Items()
|
||||
{
|
||||
var number = 0;
|
||||
[TestMethod]
|
||||
public void Wait_For_Items()
|
||||
{
|
||||
var number = 0;
|
||||
|
||||
var queue = new BlockingQueue<int>();
|
||||
var queue = new BlockingQueue<int>();
|
||||
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Run(async () =>
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
});
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
while (number < 50)
|
||||
{
|
||||
queue.Dequeue();
|
||||
Interlocked.Increment(ref number);
|
||||
queue.Enqueue(1);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(OperationCanceledException))]
|
||||
public void Use_Disposed_Queue()
|
||||
{
|
||||
var queue = new BlockingQueue<int>();
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
queue.Dispose();
|
||||
});
|
||||
});
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
queue.Dequeue(new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token);
|
||||
while (number < 50)
|
||||
{
|
||||
queue.Dequeue();
|
||||
Interlocked.Increment(ref number);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(OperationCanceledException))]
|
||||
public void Use_Disposed_Queue()
|
||||
{
|
||||
var queue = new BlockingQueue<int>();
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
queue.Dispose();
|
||||
});
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
queue.Dequeue(new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token);
|
||||
}
|
||||
}
|
||||
@@ -11,71 +11,71 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
namespace MQTTnet.Tests.Internal;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class CrossPlatformSocket_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class CrossPlatformSocket_Tests
|
||||
[TestMethod]
|
||||
public async Task Connect_Send_Receive()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Connect_Send_Receive()
|
||||
{
|
||||
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);
|
||||
await crossPlatformSocket.ConnectAsync(new DnsEndPoint("www.google.de", 80), CancellationToken.None);
|
||||
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);
|
||||
await crossPlatformSocket.ConnectAsync(new DnsEndPoint("www.github.com", 80), CancellationToken.None);
|
||||
|
||||
var requestBuffer = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\nHost: www.google.de\r\n\r\n");
|
||||
await crossPlatformSocket.SendAsync(new ArraySegment<byte>(requestBuffer), System.Net.Sockets.SocketFlags.None);
|
||||
var requestBuffer = "GET / HTTP/1.1\r\nHost: www.github.com\r\n\r\n"u8.ToArray();
|
||||
await crossPlatformSocket.SendAsync(new ArraySegment<byte>(requestBuffer), SocketFlags.None);
|
||||
|
||||
var buffer = new byte[1024];
|
||||
var length = await crossPlatformSocket.ReceiveAsync(new ArraySegment<byte>(buffer), System.Net.Sockets.SocketFlags.None);
|
||||
crossPlatformSocket.Dispose();
|
||||
var buffer = new byte[1024];
|
||||
var length = await crossPlatformSocket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
||||
crossPlatformSocket.Dispose();
|
||||
|
||||
var responseText = Encoding.UTF8.GetString(buffer, 0, length);
|
||||
var responseText = Encoding.UTF8.GetString(buffer, 0, length);
|
||||
|
||||
Assert.IsTrue(responseText.Contains("HTTP/1.1 200 OK"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(OperationCanceledException))]
|
||||
public async Task Try_Connect_Invalid_Host()
|
||||
{
|
||||
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);
|
||||
|
||||
var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
cancellationToken.Token.Register(() => crossPlatformSocket.Dispose());
|
||||
|
||||
await crossPlatformSocket.ConnectAsync(new DnsEndPoint("www.google.de", 54321), cancellationToken.Token);
|
||||
}
|
||||
|
||||
//[TestMethod]
|
||||
//public async Task Use_Disconnected_Socket()
|
||||
//{
|
||||
// var crossPlatformSocket = new CrossPlatformSocket();
|
||||
|
||||
// await crossPlatformSocket.ConnectAsync("www.google.de", 80);
|
||||
|
||||
// var requestBuffer = Encoding.UTF8.GetBytes("GET /wrong_uri HTTP/1.1\r\nConnection: close\r\n\r\n");
|
||||
// await crossPlatformSocket.SendAsync(new ArraySegment<byte>(requestBuffer), System.Net.Sockets.SocketFlags.None);
|
||||
|
||||
// var buffer = new byte[64000];
|
||||
// var length = await crossPlatformSocket.ReceiveAsync(new ArraySegment<byte>(buffer), System.Net.Sockets.SocketFlags.None);
|
||||
|
||||
// await Task.Delay(500);
|
||||
|
||||
// await crossPlatformSocket.SendAsync(new ArraySegment<byte>(requestBuffer), System.Net.Sockets.SocketFlags.None);
|
||||
//}
|
||||
|
||||
[TestMethod]
|
||||
public void Set_Options()
|
||||
{
|
||||
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);
|
||||
|
||||
Assert.IsFalse(crossPlatformSocket.ReuseAddress);
|
||||
crossPlatformSocket.ReuseAddress = true;
|
||||
Assert.IsTrue(crossPlatformSocket.ReuseAddress);
|
||||
|
||||
Assert.IsFalse(crossPlatformSocket.NoDelay);
|
||||
crossPlatformSocket.NoDelay = true;
|
||||
Assert.IsTrue(crossPlatformSocket.NoDelay);
|
||||
}
|
||||
Assert.IsTrue(responseText.Contains("HTTP/1.1"));
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(OperationCanceledException))]
|
||||
public async Task Try_Connect_Invalid_Host()
|
||||
{
|
||||
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);
|
||||
|
||||
var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
cancellationToken.Token.Register(() => crossPlatformSocket.Dispose());
|
||||
|
||||
await crossPlatformSocket.ConnectAsync(new DnsEndPoint("www.github.com", 54321), cancellationToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// [TestMethod]
|
||||
// public async Task Use_Disconnected_Socket()
|
||||
// {
|
||||
// var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);
|
||||
//
|
||||
// await crossPlatformSocket.ConnectAsync("www.github.com", 80);
|
||||
//
|
||||
// var requestBuffer = Encoding.UTF8.GetBytes("GET /wrong_uri HTTP/1.1\r\nConnection: close\r\n\r\n");
|
||||
// await crossPlatformSocket.SendAsync(new ArraySegment<byte>(requestBuffer), System.Net.Sockets.SocketFlags.None);
|
||||
//
|
||||
// var buffer = new byte[64000];
|
||||
// var length = await crossPlatformSocket.ReceiveAsync(new ArraySegment<byte>(buffer), System.Net.Sockets.SocketFlags.None);
|
||||
//
|
||||
// await Task.Delay(500);
|
||||
//
|
||||
// await crossPlatformSocket.SendAsync(new ArraySegment<byte>(requestBuffer), System.Net.Sockets.SocketFlags.None);
|
||||
// }
|
||||
|
||||
[TestMethod]
|
||||
public void Set_Options()
|
||||
{
|
||||
var crossPlatformSocket = new CrossPlatformSocket(ProtocolType.Tcp);
|
||||
|
||||
Assert.IsFalse(crossPlatformSocket.ReuseAddress);
|
||||
crossPlatformSocket.ReuseAddress = true;
|
||||
Assert.IsTrue(crossPlatformSocket.ReuseAddress);
|
||||
|
||||
Assert.IsFalse(crossPlatformSocket.NoDelay);
|
||||
crossPlatformSocket.NoDelay = true;
|
||||
Assert.IsTrue(crossPlatformSocket.NoDelay);
|
||||
}
|
||||
}
|
||||
@@ -7,37 +7,37 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
namespace MQTTnet.Tests.Internal;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttPacketBusItem_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketBusItem_Tests
|
||||
[TestMethod]
|
||||
public void Fire_Completed_Event()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Fire_Completed_Event()
|
||||
var eventFired = false;
|
||||
|
||||
var item = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
item.Completed += (_, _) =>
|
||||
{
|
||||
var eventFired = false;
|
||||
eventFired = true;
|
||||
};
|
||||
|
||||
var item = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
item.Completed += (_, __) =>
|
||||
{
|
||||
eventFired = true;
|
||||
};
|
||||
item.Complete();
|
||||
|
||||
item.Complete();
|
||||
Assert.IsTrue(eventFired);
|
||||
}
|
||||
|
||||
Assert.IsTrue(eventFired);
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Wait_Packet_Bus_Item_After_Already_Canceled()
|
||||
{
|
||||
var item = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Wait_Packet_Bus_Item_After_Already_Canceled()
|
||||
{
|
||||
var item = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
// Finish the item before the actual
|
||||
item.Cancel();
|
||||
|
||||
// Finish the item before the actual
|
||||
item.Cancel();
|
||||
|
||||
await item.WaitAsync();
|
||||
}
|
||||
await item.WaitAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -3,149 +3,146 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Tests.Internal
|
||||
namespace MQTTnet.Tests.Internal;
|
||||
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
[TestClass]
|
||||
public sealed class MqttPacketBus_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttPacketBus_Tests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Alternate_Priorities()
|
||||
[TestMethod]
|
||||
public void Alternate_Priorities()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
|
||||
Assert.AreEqual(9, bus.TotalItemsCount);
|
||||
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
|
||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Await_Single_Packet()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
var delivered = false;
|
||||
|
||||
var item1 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
var item2 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
|
||||
var item3 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
item3.Completed += (_, _) =>
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
delivered = true;
|
||||
};
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item1, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item2, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item3, MqttPacketBusPartition.Data);
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttSubAckPacket()), MqttPacketBusPartition.Control);
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPingRespPacket()), MqttPacketBusPartition.Health);
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
Assert.AreEqual(9, bus.TotalItemsCount);
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPublishPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttSubAckPacket));
|
||||
Assert.IsInstanceOfType(bus.DequeueItemAsync(CancellationToken.None).Result.Packet, typeof(MqttPingRespPacket));
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||
}
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
[TestMethod]
|
||||
public void Await_Single_Packet()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
var delivered = false;
|
||||
// The third packet has the event attached.
|
||||
Assert.IsTrue(delivered);
|
||||
}
|
||||
|
||||
var item1 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
var item2 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
[TestMethod]
|
||||
public void Export_Packets_Without_Dequeue()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
var item3 = new MqttPacketBusItem(new MqttPublishPacket());
|
||||
item3.Completed += (_, __) =>
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||
|
||||
var exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Control);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Health);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Data);
|
||||
Assert.AreEqual(3, exportedPackets.Count);
|
||||
|
||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fill_From_Different_Task()
|
||||
{
|
||||
const int messageCount = 500;
|
||||
|
||||
var delayRandom = new Random();
|
||||
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
_ = Task.Run(
|
||||
() =>
|
||||
{
|
||||
delivered = true;
|
||||
};
|
||||
|
||||
bus.EnqueueItem(item1, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item2, MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(item3, MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
Assert.IsFalse(delivered);
|
||||
|
||||
bus.DequeueItemAsync(CancellationToken.None).Result.Complete();
|
||||
|
||||
// The third packet has the event attached.
|
||||
Assert.IsTrue(delivered);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Export_Packets_Without_Dequeue()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(new MqttPublishPacket()), MqttPacketBusPartition.Data);
|
||||
|
||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||
|
||||
var exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Control);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Health);
|
||||
Assert.AreEqual(0, exportedPackets.Count);
|
||||
|
||||
exportedPackets = bus.ExportPackets(MqttPacketBusPartition.Data);
|
||||
Assert.AreEqual(3, exportedPackets.Count);
|
||||
|
||||
Assert.AreEqual(3, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fill_From_Different_Task()
|
||||
{
|
||||
const int MessageCount = 500;
|
||||
|
||||
var delayRandom = new Random();
|
||||
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
_ = Task.Run(
|
||||
() =>
|
||||
for (var i = 0; i < messageCount; i++)
|
||||
{
|
||||
for (var i = 0; i < MessageCount; i++)
|
||||
{
|
||||
bus.EnqueueItem(new MqttPacketBusItem(MqttPingReqPacket.Instance), MqttPacketBusPartition.Health);
|
||||
bus.EnqueueItem(new MqttPacketBusItem(MqttPingReqPacket.Instance), MqttPacketBusPartition.Health);
|
||||
|
||||
Thread.Sleep(delayRandom.Next(0, 10));
|
||||
}
|
||||
});
|
||||
|
||||
for (var i = 0; i < MessageCount; i++)
|
||||
{
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
||||
{
|
||||
await bus.DequeueItemAsync(timeout.Token);
|
||||
Thread.Sleep(delayRandom.Next(0, 10));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Wait_With_Empty_Bus()
|
||||
for (var i = 0; i < messageCount; i++)
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1)))
|
||||
{
|
||||
await bus.DequeueItemAsync(timeout.Token);
|
||||
}
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
await bus.DequeueItemAsync(timeout.Token);
|
||||
}
|
||||
|
||||
Assert.AreEqual(0, bus.TotalItemsCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(TaskCanceledException))]
|
||||
public async Task Wait_With_Empty_Bus()
|
||||
{
|
||||
var bus = new MqttPacketBus();
|
||||
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1));
|
||||
await bus.DequeueItemAsync(timeout.Token);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.8.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.8.2" />
|
||||
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
|
||||
<PackageReference Include="MSTest" Version="3.6.4"/>
|
||||
|
||||
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.IO;
|
||||
@@ -13,370 +13,337 @@ using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.MQTTv5
|
||||
namespace MQTTnet.Tests.MQTTv5;
|
||||
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
[TestClass]
|
||||
public sealed class Client_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Client_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Connect()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Connect_With_New_Mqtt_Features()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
// This test can be also executed against "broker.hivemq.com" to validate package format.
|
||||
var client = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder()
|
||||
//.WithTcpServer("broker.hivemq.com")
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTopicAliasMaximum(20)
|
||||
.WithReceiveMaximum(20)
|
||||
.WithWillTopic("abc")
|
||||
.WithWillDelayInterval(20)
|
||||
.Build());
|
||||
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
|
||||
await client.SubscribeAsync("a");
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.PublishAsync(new MqttApplicationMessageBuilder()
|
||||
.WithTopic("a")
|
||||
.WithPayload("x")
|
||||
.WithUserProperty("a", "1")
|
||||
.WithUserProperty("b", "2")
|
||||
.WithPayloadFormatIndicator(MqttPayloadFormatIndicator.CharacterData)
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsNotNull(receivedMessage);
|
||||
|
||||
Assert.AreEqual(2, receivedMessage.UserProperties.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500).Build());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_And_Disconnect()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
await client.DisconnectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client =
|
||||
await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var result = await client.SubscribeAsync(new MqttClientSubscribeOptions()
|
||||
{
|
||||
SubscriptionIdentifier = 1,
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter {Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce}
|
||||
}
|
||||
});
|
||||
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(1, result.Items.Count);
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS1, result.Items.First().ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Unsubscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
await client.SubscribeAsync("a");
|
||||
|
||||
var result = await client.UnsubscribeAsync("a");
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(1, result.Items.Count);
|
||||
Assert.AreEqual(MqttClientUnsubscribeResultCode.Success, result.Items.First().ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_0_LargeBuffer()
|
||||
{
|
||||
using var recyclableMemoryStream = GetLargePayload();
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishSequenceAsync("a", recyclableMemoryStream.GetReadOnlySequence());
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_1_LargeBuffer()
|
||||
{
|
||||
using var recyclableMemoryStream = GetLargePayload();
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishSequenceAsync("a", recyclableMemoryStream.GetReadOnlySequence(), MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_2_LargeBuffer()
|
||||
{
|
||||
using var recyclableMemoryStream = GetLargePayload();
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishSequenceAsync("a", recyclableMemoryStream.GetReadOnlySequence(), MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_0()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishStringAsync("a", "b");
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_1()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_2()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_With_RecyclableMemoryStream()
|
||||
{
|
||||
var memoryManager = new RecyclableMemoryStreamManager(options: new RecyclableMemoryStreamManager.Options { BlockSize = 4096 });
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
const int payloadSize = 100000;
|
||||
using var memoryStream = memoryManager.GetStream();
|
||||
|
||||
byte testValue = 0;
|
||||
while (memoryStream.Length < payloadSize)
|
||||
{
|
||||
memoryStream.WriteByte(testValue++);
|
||||
}
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("Hello")
|
||||
.WithPayload(memoryStream.GetReadOnlySequence())
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithResponseTopic("response")
|
||||
.WithContentType("text")
|
||||
.WithMessageExpiryInterval(50)
|
||||
.WithCorrelationData(new byte[12])
|
||||
.WithTopicAlias(2)
|
||||
.Build();
|
||||
|
||||
var result = await client.PublishAsync(applicationMessage);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_With_Properties()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("Hello")
|
||||
.WithPayload("World")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithResponseTopic("response")
|
||||
.WithContentType("text")
|
||||
.WithMessageExpiryInterval(50)
|
||||
.WithCorrelationData(new byte[12])
|
||||
.WithTopicAlias(2)
|
||||
.Build();
|
||||
|
||||
var result = await client.PublishAsync(applicationMessage);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_And_Publish()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500).WithClientId("client1"));
|
||||
|
||||
var testMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
|
||||
await client1.SubscribeAsync("a");
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500).WithClientId("client2"));
|
||||
await client2.PublishStringAsync("a", "b");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(1, testMessageHandler.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("Subscribe_And_Publish_client1", testMessageHandler.ReceivedEventArgs[0].ClientId);
|
||||
Assert.AreEqual("a", testMessageHandler.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
Assert.AreEqual("b", testMessageHandler.ReceivedEventArgs[0].ApplicationMessage.ConvertPayloadToString());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_And_Receive_New_Properties()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
receiver.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("Hello")
|
||||
.WithPayload("World")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithResponseTopic("response")
|
||||
.WithContentType("text")
|
||||
.WithMessageExpiryInterval(50)
|
||||
.WithCorrelationData(new byte[12])
|
||||
.WithTopicAlias(2)
|
||||
.Build();
|
||||
|
||||
await sender.PublishAsync(applicationMessage);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsNotNull(receivedMessage);
|
||||
Assert.AreEqual(applicationMessage.Topic, receivedMessage.Topic);
|
||||
Assert.AreEqual(applicationMessage.TopicAlias, receivedMessage.TopicAlias);
|
||||
Assert.AreEqual(applicationMessage.ContentType, receivedMessage.ContentType);
|
||||
Assert.AreEqual(applicationMessage.ResponseTopic, receivedMessage.ResponseTopic);
|
||||
Assert.AreEqual(applicationMessage.MessageExpiryInterval, receivedMessage.MessageExpiryInterval);
|
||||
CollectionAssert.AreEqual(applicationMessage.CorrelationData, receivedMessage.CorrelationData);
|
||||
CollectionAssert.AreEqual(applicationMessage.Payload.ToArray(), receivedMessage.Payload.ToArray());
|
||||
CollectionAssert.AreEqual(applicationMessage.UserProperties, receivedMessage.UserProperties);
|
||||
}
|
||||
}
|
||||
|
||||
private RecyclableMemoryStream GetLargePayload()
|
||||
{
|
||||
var memoryManager = new RecyclableMemoryStreamManager();
|
||||
var memoryStream = memoryManager.GetStream();
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
memoryStream.WriteByte((byte)i);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
return memoryStream;
|
||||
}
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500).Build());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_And_Disconnect()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
await client.DisconnectAsync();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_With_New_Mqtt_Features()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
// This test can be also executed against "broker.hivemq.com" to validate package format.
|
||||
var client = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder()
|
||||
//.WithTcpServer("broker.hivemq.com")
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTopicAliasMaximum(20)
|
||||
.WithReceiveMaximum(20)
|
||||
.WithWillTopic("abc")
|
||||
.WithWillDelayInterval(20)
|
||||
.Build());
|
||||
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
|
||||
await client.SubscribeAsync("a");
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("a")
|
||||
.WithPayload("x")
|
||||
.WithUserProperty("a", "1")
|
||||
.WithUserProperty("b", "2")
|
||||
.WithPayloadFormatIndicator(MqttPayloadFormatIndicator.CharacterData)
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsNotNull(receivedMessage);
|
||||
|
||||
Assert.AreEqual(2, receivedMessage.UserProperties.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_And_Receive_New_Properties()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
receiver.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("Hello")
|
||||
.WithPayload("World")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithResponseTopic("response")
|
||||
.WithContentType("text")
|
||||
.WithMessageExpiryInterval(50)
|
||||
.WithCorrelationData(new byte[12])
|
||||
.WithTopicAlias(2)
|
||||
.Build();
|
||||
|
||||
await sender.PublishAsync(applicationMessage);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsNotNull(receivedMessage);
|
||||
Assert.AreEqual(applicationMessage.Topic, receivedMessage.Topic);
|
||||
Assert.AreEqual(applicationMessage.TopicAlias, receivedMessage.TopicAlias);
|
||||
Assert.AreEqual(applicationMessage.ContentType, receivedMessage.ContentType);
|
||||
Assert.AreEqual(applicationMessage.ResponseTopic, receivedMessage.ResponseTopic);
|
||||
Assert.AreEqual(applicationMessage.MessageExpiryInterval, receivedMessage.MessageExpiryInterval);
|
||||
CollectionAssert.AreEqual(applicationMessage.CorrelationData, receivedMessage.CorrelationData);
|
||||
CollectionAssert.AreEqual(applicationMessage.Payload.ToArray(), receivedMessage.Payload.ToArray());
|
||||
CollectionAssert.AreEqual(applicationMessage.UserProperties, receivedMessage.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_0()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishStringAsync("a", "b");
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_0_LargeBuffer()
|
||||
{
|
||||
await using var recyclableMemoryStream = GetLargePayload();
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishSequenceAsync("a", recyclableMemoryStream.GetReadOnlySequence());
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_1()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_1_LargeBuffer()
|
||||
{
|
||||
await using var recyclableMemoryStream = GetLargePayload();
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishSequenceAsync("a", recyclableMemoryStream.GetReadOnlySequence(), MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_2()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishStringAsync("a", "b", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_2_LargeBuffer()
|
||||
{
|
||||
await using var recyclableMemoryStream = GetLargePayload();
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var result = await client.PublishSequenceAsync("a", recyclableMemoryStream.GetReadOnlySequence(), MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, result.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_With_Properties()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("Hello")
|
||||
.WithPayload("World")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithResponseTopic("response")
|
||||
.WithContentType("text")
|
||||
.WithMessageExpiryInterval(50)
|
||||
.WithCorrelationData(new byte[12])
|
||||
.WithTopicAlias(2)
|
||||
.Build();
|
||||
|
||||
var result = await client.PublishAsync(applicationMessage);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_With_RecyclableMemoryStream()
|
||||
{
|
||||
var memoryManager = new RecyclableMemoryStreamManager(new RecyclableMemoryStreamManager.Options { BlockSize = 4096 });
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
const int payloadSize = 100000;
|
||||
await using var memoryStream = memoryManager.GetStream();
|
||||
|
||||
byte testValue = 0;
|
||||
while (memoryStream.Length < payloadSize)
|
||||
{
|
||||
memoryStream.WriteByte(testValue++);
|
||||
}
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("Hello")
|
||||
.WithPayload(memoryStream.GetReadOnlySequence())
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithResponseTopic("response")
|
||||
.WithContentType("text")
|
||||
.WithMessageExpiryInterval(50)
|
||||
.WithCorrelationData(new byte[12])
|
||||
.WithTopicAlias(2)
|
||||
.Build();
|
||||
|
||||
var result = await client.PublishAsync(applicationMessage);
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, result.ReasonCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var result = await client.SubscribeAsync(
|
||||
new MqttClientSubscribeOptions
|
||||
{
|
||||
SubscriptionIdentifier = 1,
|
||||
TopicFilters =
|
||||
[
|
||||
new MqttTopicFilter() { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }
|
||||
]
|
||||
});
|
||||
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(1, result.Items.Count);
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS1, result.Items.First().ResultCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_And_Publish()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500).WithClientId("client1"));
|
||||
|
||||
var testMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
|
||||
await client1.SubscribeAsync("a");
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500).WithClientId("client2"));
|
||||
await client2.PublishStringAsync("a", "b");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(1, testMessageHandler.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("Subscribe_And_Publish_client1", testMessageHandler.ReceivedEventArgs[0].ClientId);
|
||||
Assert.AreEqual("a", testMessageHandler.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
Assert.AreEqual("b", testMessageHandler.ReceivedEventArgs[0].ApplicationMessage.ConvertPayloadToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Unsubscribe()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
await client.SubscribeAsync("a");
|
||||
|
||||
var result = await client.UnsubscribeAsync("a");
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.AreEqual(1, result.Items.Count);
|
||||
Assert.AreEqual(MqttClientUnsubscribeResultCode.Success, result.Items.First().ResultCode);
|
||||
}
|
||||
|
||||
static RecyclableMemoryStream GetLargePayload()
|
||||
{
|
||||
var memoryManager = new RecyclableMemoryStreamManager();
|
||||
var memoryStream = memoryManager.GetStream();
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
memoryStream.WriteByte((byte)i);
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
return memoryStream;
|
||||
}
|
||||
}
|
||||
@@ -11,221 +11,210 @@ using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.MQTTv5
|
||||
namespace MQTTnet.Tests.MQTTv5;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Server_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Server_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Will_Message_Send()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Will_Message_Send()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce).WithProtocolVersion(MqttProtocolVersion.V500);
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var receivedMessagesCount = 0;
|
||||
c1.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce).WithProtocolVersion(MqttProtocolVersion.V500);
|
||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var c2 = await testEnvironment.ConnectClient(clientOptions);
|
||||
c2.Dispose(); // Dispose will not send a DISCONNECT packet first so the will message must be sent.
|
||||
|
||||
var receivedMessagesCount = 0;
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
await LongTestDelay();
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient(clientOptions);
|
||||
c2.Dispose(); // Dispose will not send a DISCONNECT packet first so the will message must be sent.
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Validate_IsSessionPresent()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
// Create server with persistent sessions enabled
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string ClientId = "Client1";
|
||||
|
||||
// Create client without clean session and long session expiry interval
|
||||
var options1 = CreateClientOptions(testEnvironment, ClientId, false, 9999);
|
||||
var client1 = await testEnvironment.ConnectClient(options1);
|
||||
|
||||
// Disconnect; empty session should remain on server
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
// Simulate some time delay between connections
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID to existing session
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options2 = CreateClientOptions(testEnvironment, ClientId, false, 9999);
|
||||
var result = await client2.ConnectAsync(options2).ConfigureAwait(false);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
|
||||
// Session should be present
|
||||
|
||||
Assert.IsTrue(result.IsSessionPresent, "Session not present");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_with_Undefined_SessionExpiryInterval()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
// Create server with persistent sessions enabled
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string ClientId = "Client1";
|
||||
|
||||
// Create client without clean session and NO session expiry interval,
|
||||
// that means, the session should not persist
|
||||
|
||||
var options1 = CreateClientOptions(testEnvironment, ClientId, false, 0);
|
||||
var client1 = await testEnvironment.ConnectClient(options1);
|
||||
|
||||
// Disconnect; no session should remain on server because the session expiry interval was undefined
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
// Simulate some time delay between connections
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID to existing session
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options2 = CreateClientOptions(testEnvironment, ClientId, false, 9999);
|
||||
var result = await client2.ConnectAsync(options2).ConfigureAwait(false);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
|
||||
// Session should not be present
|
||||
|
||||
Assert.IsTrue(!result.IsSessionPresent, "Session is present when it should not");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public async Task Reconnect_with_different_SessionExpiryInterval()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
// Create server with persistent sessions enabled
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string ClientId = "Client1";
|
||||
|
||||
// Create client with clean session and session expiry interval > 0
|
||||
|
||||
var options = CreateClientOptions(testEnvironment, ClientId, true, 9999);
|
||||
var client1 = await testEnvironment.ConnectClient(options);
|
||||
|
||||
// Disconnect; session should remain on server
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID to the existing session but leave session expiry interval undefined this time.
|
||||
// Session should be present because the client1 connection had SessionExpiryInterval > 0
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options2 = CreateClientOptions(testEnvironment, ClientId, false, 0);
|
||||
var result2 = await client2.ConnectAsync(options2).ConfigureAwait(false);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
|
||||
Assert.IsTrue(result2.IsSessionPresent, "Session is not present when it should");
|
||||
|
||||
// Simulate some time delay between connections
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID.
|
||||
// No session should be present because the previous session expiry interval was undefined for the client2 connection
|
||||
|
||||
var client3 = testEnvironment.CreateClient();
|
||||
var options3 = CreateClientOptions(testEnvironment, ClientId, false, 0);
|
||||
var result3 = await client2.ConnectAsync(options3).ConfigureAwait(false);
|
||||
|
||||
await client3.DisconnectAsync();
|
||||
|
||||
// Session should be present
|
||||
|
||||
Assert.IsTrue(!result3.IsSessionPresent, "Session is present when it should not");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_with_Reason()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var disconnectReason = MqttClientDisconnectReason.UnspecifiedError;
|
||||
|
||||
string testClientId = null;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
testEnvironment.Server.ClientConnectedAsync += e =>
|
||||
{
|
||||
testClientId = e.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
disconnectReason = e.Reason;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// Test client should be connected now
|
||||
|
||||
Assert.IsTrue(testClientId != null);
|
||||
|
||||
// Have the server disconnect the client with AdministrativeAction reason
|
||||
|
||||
await testEnvironment.Server.DisconnectClientAsync(testClientId, MqttDisconnectReasonCode.AdministrativeAction);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// The reason should be returned to the client in the DISCONNECT packet
|
||||
|
||||
Assert.AreEqual(MqttClientDisconnectReason.AdministrativeAction, disconnectReason);
|
||||
}
|
||||
}
|
||||
|
||||
static MqttClientOptions CreateClientOptions(TestEnvironment testEnvironment, string clientId, bool cleanSession, uint sessionExpiryInterval)
|
||||
{
|
||||
return testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(sessionExpiryInterval)
|
||||
.WithCleanSession(cleanSession)
|
||||
.WithClientId(clientId)
|
||||
.Build();
|
||||
}
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Validate_IsSessionPresent()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
// Create server with persistent sessions enabled
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string clientId = "Client1";
|
||||
|
||||
// Create client without clean session and long session expiry interval
|
||||
var options1 = CreateClientOptions(testEnvironment, clientId, false, 9999);
|
||||
var client1 = await testEnvironment.ConnectClient(options1);
|
||||
|
||||
// Disconnect; empty session should remain on server
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
// Simulate some time delay between connections
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID to existing session
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options2 = CreateClientOptions(testEnvironment, clientId, false, 9999);
|
||||
var result = await client2.ConnectAsync(options2).ConfigureAwait(false);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
|
||||
// Session should be present
|
||||
|
||||
Assert.IsTrue(result.IsSessionPresent, "Session not present");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_with_Undefined_SessionExpiryInterval()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
// Create server with persistent sessions enabled
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string clientId = "Client1";
|
||||
|
||||
// Create client without clean session and NO session expiry interval,
|
||||
// that means, the session should not persist
|
||||
|
||||
var options1 = CreateClientOptions(testEnvironment, clientId, false, 0);
|
||||
var client1 = await testEnvironment.ConnectClient(options1);
|
||||
|
||||
// Disconnect; no session should remain on server because the session expiry interval was undefined
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
// Simulate some time delay between connections
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID to existing session
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options2 = CreateClientOptions(testEnvironment, clientId, false, 9999);
|
||||
var result = await client2.ConnectAsync(options2).ConfigureAwait(false);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
|
||||
// Session should not be present
|
||||
|
||||
Assert.IsTrue(!result.IsSessionPresent, "Session is present when it should not");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Reconnect_with_different_SessionExpiryInterval()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
// Create server with persistent sessions enabled
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string clientId = "Client1";
|
||||
|
||||
// Create client with clean session and session expiry interval > 0
|
||||
|
||||
var options = CreateClientOptions(testEnvironment, clientId, true, 9999);
|
||||
var client1 = await testEnvironment.ConnectClient(options);
|
||||
|
||||
// Disconnect; session should remain on server
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID to the existing session but leave session expiry interval undefined this time.
|
||||
// Session should be present because the client1 connection had SessionExpiryInterval > 0
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options2 = CreateClientOptions(testEnvironment, clientId, false, 0);
|
||||
var result2 = await client2.ConnectAsync(options2).ConfigureAwait(false);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
|
||||
Assert.IsTrue(result2.IsSessionPresent, "Session is not present when it should");
|
||||
|
||||
// Simulate some time delay between connections
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID.
|
||||
// No session should be present because the previous session expiry interval was undefined for the client2 connection
|
||||
|
||||
var client3 = testEnvironment.CreateClient();
|
||||
var options3 = CreateClientOptions(testEnvironment, clientId, false, 0);
|
||||
var result3 = await client2.ConnectAsync(options3).ConfigureAwait(false);
|
||||
|
||||
await client3.DisconnectAsync();
|
||||
|
||||
// Session should be present
|
||||
|
||||
Assert.IsTrue(!result3.IsSessionPresent, "Session is present when it should not");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_with_Reason()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var disconnectReason = MqttClientDisconnectReason.UnspecifiedError;
|
||||
|
||||
string testClientId = null;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
testEnvironment.Server.ClientConnectedAsync += e =>
|
||||
{
|
||||
testClientId = e.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
disconnectReason = e.Reason;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// Test client should be connected now
|
||||
|
||||
Assert.IsTrue(testClientId != null);
|
||||
|
||||
// Have the server disconnect the client with AdministrativeAction reason
|
||||
|
||||
await testEnvironment.Server.DisconnectClientAsync(testClientId, MqttDisconnectReasonCode.AdministrativeAction);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// The reason should be returned to the client in the DISCONNECT packet
|
||||
|
||||
Assert.AreEqual(MqttClientDisconnectReason.AdministrativeAction, disconnectReason);
|
||||
}
|
||||
|
||||
static MqttClientOptions CreateClientOptions(TestEnvironment testEnvironment, string clientId, bool cleanSession, uint sessionExpiryInterval)
|
||||
{
|
||||
return testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(sessionExpiryInterval)
|
||||
.WithCleanSession(cleanSession)
|
||||
.WithClientId(clientId)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Internal;
|
||||
using System.Buffers;
|
||||
@@ -12,53 +11,52 @@ using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Tests.Mockups
|
||||
namespace MQTTnet.Tests.Mockups;
|
||||
|
||||
public sealed class MemoryMqttChannel : IMqttChannel
|
||||
{
|
||||
public sealed class MemoryMqttChannel : IMqttChannel
|
||||
readonly MemoryStream _stream;
|
||||
|
||||
public MemoryMqttChannel(MemoryStream stream)
|
||||
{
|
||||
readonly MemoryStream _stream;
|
||||
_stream = stream;
|
||||
}
|
||||
|
||||
public MemoryMqttChannel(MemoryStream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
}
|
||||
|
||||
public MemoryMqttChannel(byte[] buffer)
|
||||
{
|
||||
_stream = new MemoryStream(buffer);
|
||||
}
|
||||
|
||||
public EndPoint RemoteEndPoint { get; set; }
|
||||
|
||||
public bool IsSecureConnection { get; } = false;
|
||||
|
||||
public X509Certificate2 ClientCertificate { get; }
|
||||
|
||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return _stream.ReadAsync(buffer, offset, count, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(ReadOnlySequence<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var segment in buffer)
|
||||
{
|
||||
await _stream.WriteAsync(segment, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public MemoryMqttChannel(byte[] buffer)
|
||||
{
|
||||
_stream = new MemoryStream(buffer);
|
||||
}
|
||||
|
||||
public EndPoint RemoteEndPoint { get; set; }
|
||||
|
||||
public bool IsSecureConnection { get; } = false;
|
||||
|
||||
public X509Certificate2 ClientCertificate { get; set; }
|
||||
|
||||
public Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return _stream.ReadAsync(buffer, offset, count, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task WriteAsync(ReadOnlySequence<byte> buffer, bool isEndOfPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var segment in buffer)
|
||||
{
|
||||
await _stream.WriteAsync(segment, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -10,72 +10,71 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Mockups
|
||||
namespace MQTTnet.Tests.Mockups;
|
||||
|
||||
public sealed class TestApplicationMessageReceivedHandler
|
||||
{
|
||||
public sealed class TestApplicationMessageReceivedHandler
|
||||
readonly List<MqttApplicationMessageReceivedEventArgs> _receivedEventArgs = new List<MqttApplicationMessageReceivedEventArgs>();
|
||||
|
||||
public TestApplicationMessageReceivedHandler(IMqttClient mqttClient)
|
||||
{
|
||||
readonly List<MqttApplicationMessageReceivedEventArgs> _receivedEventArgs = new List<MqttApplicationMessageReceivedEventArgs>();
|
||||
ArgumentNullException.ThrowIfNull(mqttClient);
|
||||
|
||||
public TestApplicationMessageReceivedHandler(IMqttClient mqttClient)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(mqttClient);
|
||||
mqttClient.ApplicationMessageReceivedAsync += OnApplicationMessageReceivedAsync;
|
||||
}
|
||||
|
||||
mqttClient.ApplicationMessageReceivedAsync += OnApplicationMessageReceivedAsync;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
return _receivedEventArgs.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<MqttApplicationMessageReceivedEventArgs> ReceivedEventArgs
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
return _receivedEventArgs.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AssertReceivedCountEquals(int expectedCount)
|
||||
{
|
||||
Assert.AreEqual(expectedCount, Count);
|
||||
}
|
||||
|
||||
public string GeneratePayloadSequence()
|
||||
{
|
||||
var sequence = new StringBuilder();
|
||||
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
foreach (var receivedEventArg in _receivedEventArgs)
|
||||
{
|
||||
var payload = receivedEventArg.ApplicationMessage.ConvertPayloadToString();
|
||||
|
||||
// An empty payload is not part of the sequence!
|
||||
sequence.Append(payload);
|
||||
}
|
||||
}
|
||||
|
||||
return sequence.ToString();
|
||||
}
|
||||
|
||||
Task OnApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
_receivedEventArgs.Add(eventArgs);
|
||||
return _receivedEventArgs.Count;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public List<MqttApplicationMessageReceivedEventArgs> ReceivedEventArgs
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
return _receivedEventArgs.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AssertReceivedCountEquals(int expectedCount)
|
||||
{
|
||||
Assert.AreEqual(expectedCount, Count);
|
||||
}
|
||||
|
||||
public string GeneratePayloadSequence()
|
||||
{
|
||||
var sequence = new StringBuilder();
|
||||
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
foreach (var receivedEventArg in _receivedEventArgs)
|
||||
{
|
||||
var payload = receivedEventArg.ApplicationMessage.ConvertPayloadToString();
|
||||
|
||||
// An empty payload is not part of the sequence!
|
||||
sequence.Append(payload);
|
||||
}
|
||||
}
|
||||
|
||||
return sequence.ToString();
|
||||
}
|
||||
|
||||
Task OnApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
lock (_receivedEventArgs)
|
||||
{
|
||||
_receivedEventArgs.Add(eventArgs);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
}
|
||||
@@ -17,422 +17,410 @@ using MQTTnet.LowLevelClient;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Mockups
|
||||
namespace MQTTnet.Tests.Mockups;
|
||||
|
||||
public sealed class TestEnvironment : IDisposable
|
||||
{
|
||||
public sealed class TestEnvironment : IDisposable
|
||||
readonly List<string> _clientErrors = new();
|
||||
readonly List<IMqttClient> _clients = new();
|
||||
readonly List<Exception> _exceptions = new();
|
||||
readonly List<ILowLevelMqttClient> _lowLevelClients = new();
|
||||
readonly MqttProtocolVersion _protocolVersion;
|
||||
readonly List<string> _serverErrors = new();
|
||||
|
||||
public TestEnvironment() : this(null)
|
||||
{
|
||||
readonly List<string> _clientErrors = new();
|
||||
readonly List<IMqttClient> _clients = new();
|
||||
readonly List<Exception> _exceptions = new();
|
||||
readonly List<ILowLevelMqttClient> _lowLevelClients = new();
|
||||
readonly MqttProtocolVersion _protocolVersion;
|
||||
readonly List<string> _serverErrors = new();
|
||||
}
|
||||
|
||||
public TestEnvironment() : this(null)
|
||||
public TestEnvironment(
|
||||
TestContext testContext, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, bool trackUnobservedTaskException = true)
|
||||
{
|
||||
_protocolVersion = protocolVersion;
|
||||
TestContext = testContext;
|
||||
|
||||
if (trackUnobservedTaskException)
|
||||
{
|
||||
TaskScheduler.UnobservedTaskException += TrackUnobservedTaskException;
|
||||
}
|
||||
|
||||
public TestEnvironment(
|
||||
TestContext testContext, MqttProtocolVersion protocolVersion = MqttProtocolVersion.V311, bool trackUnobservedTaskException = true)
|
||||
ServerLogger.LogMessagePublished += (_, e) =>
|
||||
{
|
||||
_protocolVersion = protocolVersion;
|
||||
TestContext = testContext;
|
||||
Debug.WriteLine(e.LogMessage.ToString());
|
||||
|
||||
if (trackUnobservedTaskException)
|
||||
{
|
||||
TaskScheduler.UnobservedTaskException += TrackUnobservedTaskException;
|
||||
}
|
||||
|
||||
ServerLogger.LogMessagePublished += (s, e) =>
|
||||
{
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
Debug.WriteLine(e.LogMessage.ToString());
|
||||
}
|
||||
|
||||
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
||||
{
|
||||
lock (_serverErrors)
|
||||
{
|
||||
_serverErrors.Add(e.LogMessage.ToString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ClientLogger.LogMessagePublished += (s, e) =>
|
||||
{
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
Debug.WriteLine(e.LogMessage.ToString());
|
||||
}
|
||||
|
||||
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
||||
{
|
||||
if (!IgnoreClientLogErrors)
|
||||
{
|
||||
lock (_clientErrors)
|
||||
{
|
||||
_clientErrors.Add(e.LogMessage.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public MqttNetEventLogger ClientLogger { get; } = new("client");
|
||||
|
||||
public static bool EnableLogger { get; set; } = true;
|
||||
|
||||
public MqttClientFactory ClientFactory { get; } = new();
|
||||
|
||||
public MqttServerFactory ServerFactory { get; } = new();
|
||||
|
||||
public bool IgnoreClientLogErrors { get; set; }
|
||||
|
||||
public bool IgnoreServerLogErrors { get; set; }
|
||||
|
||||
public MqttServer Server { get; private set; }
|
||||
|
||||
public MqttNetEventLogger ServerLogger { get; } = new("server");
|
||||
|
||||
public int ServerPort { get; set; }
|
||||
|
||||
public TestContext TestContext { get; }
|
||||
|
||||
public Task<IMqttClient> ConnectClient()
|
||||
{
|
||||
return ConnectClient(ClientFactory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion));
|
||||
}
|
||||
|
||||
public async Task<IMqttClient> ConnectClient(Action<MqttClientOptionsBuilder> configureOptions, TimeSpan timeout = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(configureOptions);
|
||||
|
||||
// Start with initial default values.
|
||||
var optionsBuilder = ClientFactory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort);
|
||||
|
||||
// Let the caller override settings. Do not touch the options after this.
|
||||
configureOptions.Invoke(optionsBuilder);
|
||||
|
||||
var options = optionsBuilder.Build();
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeoutToken = new CancellationTokenSource(timeout))
|
||||
{
|
||||
await client.ConnectAsync(options, timeoutToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public async Task<IMqttClient> ConnectClient(MqttClientOptionsBuilder options, TimeSpan timeout = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
options = options.WithTcpServer("127.0.0.1", ServerPort);
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await client.ConnectAsync(options.Build()).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeoutToken = new CancellationTokenSource(timeout))
|
||||
{
|
||||
await client.ConnectAsync(options.Build(), timeoutToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public async Task<IMqttClient> ConnectClient(MqttClientOptions options, TimeSpan timeout = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var timeoutToken = new CancellationTokenSource(timeout))
|
||||
{
|
||||
await client.ConnectAsync(options, timeoutToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public async Task<ILowLevelMqttClient> ConnectLowLevelClient(Action<MqttClientOptionsBuilder> optionsBuilder = null)
|
||||
{
|
||||
var options = new MqttClientOptionsBuilder();
|
||||
options = options.WithTcpServer("127.0.0.1", ServerPort);
|
||||
optionsBuilder?.Invoke(options);
|
||||
|
||||
var client = CreateLowLevelClient();
|
||||
await client.ConnectAsync(options.Build(), CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public async Task<MqttRpcClient> ConnectRpcClient(MqttRpcClientOptions options)
|
||||
{
|
||||
return new MqttRpcClient(await ConnectClient(), options);
|
||||
}
|
||||
|
||||
public TestApplicationMessageReceivedHandler CreateApplicationMessageHandler(IMqttClient mqttClient)
|
||||
{
|
||||
return new TestApplicationMessageReceivedHandler(mqttClient);
|
||||
}
|
||||
|
||||
public IMqttClient CreateClient()
|
||||
{
|
||||
var logger = EnableLogger ? (IMqttNetLogger)ClientLogger : MqttNetNullLogger.Instance;
|
||||
|
||||
var client = ClientFactory.CreateMqttClient(logger);
|
||||
|
||||
client.ConnectingAsync += e =>
|
||||
{
|
||||
if (TestContext != null)
|
||||
{
|
||||
var clientOptions = e.ClientOptions;
|
||||
var existingClientId = clientOptions.ClientId;
|
||||
if (existingClientId != null && !existingClientId.StartsWith(TestContext.TestName))
|
||||
{
|
||||
clientOptions.ClientId = TestContext.TestName + "_" + existingClientId;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
lock (_clients)
|
||||
{
|
||||
_clients.Add(client);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public MqttClientOptions CreateDefaultClientOptions()
|
||||
{
|
||||
return CreateDefaultClientOptionsBuilder().Build();
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder CreateDefaultClientOptionsBuilder()
|
||||
{
|
||||
return ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(_protocolVersion)
|
||||
.WithTcpServer("127.0.0.1", ServerPort)
|
||||
.WithClientId(TestContext.TestName + "_" + Guid.NewGuid());
|
||||
}
|
||||
|
||||
public ILowLevelMqttClient CreateLowLevelClient()
|
||||
{
|
||||
var client = ClientFactory.CreateLowLevelMqttClient(ClientLogger);
|
||||
|
||||
lock (_lowLevelClients)
|
||||
{
|
||||
_lowLevelClients.Add(client);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public MqttServer CreateServer(MqttServerOptions options)
|
||||
{
|
||||
if (Server != null)
|
||||
{
|
||||
throw new InvalidOperationException("Server already started.");
|
||||
}
|
||||
|
||||
var logger = EnableLogger ? (IMqttNetLogger)ServerLogger : new MqttNetNullLogger();
|
||||
|
||||
Server = ServerFactory.CreateMqttServer(options, logger);
|
||||
|
||||
Server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (TestContext != null)
|
||||
{
|
||||
// Null is used when the client id is assigned from the server!
|
||||
if (!string.IsNullOrEmpty(e.ClientId) && !e.ClientId.StartsWith(TestContext.TestName))
|
||||
{
|
||||
TrackException(new InvalidOperationException($"Invalid client ID used ({e.ClientId}). It must start with UnitTest name."));
|
||||
e.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
return Server;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_clients)
|
||||
{
|
||||
foreach (var mqttClient in _clients)
|
||||
{
|
||||
try
|
||||
{
|
||||
//mqttClient.DisconnectAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// This can happen when the test already disconnected the client.
|
||||
}
|
||||
finally
|
||||
{
|
||||
mqttClient?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
_clients.Clear();
|
||||
}
|
||||
|
||||
lock (_lowLevelClients)
|
||||
{
|
||||
foreach (var lowLevelMqttClient in _lowLevelClients)
|
||||
{
|
||||
lowLevelMqttClient.Dispose();
|
||||
}
|
||||
|
||||
_lowLevelClients.Clear();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Server?.StopAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// This can happen when the test already stopped the server.
|
||||
}
|
||||
finally
|
||||
{
|
||||
Server?.Dispose();
|
||||
}
|
||||
|
||||
Server = null;
|
||||
|
||||
ThrowIfLogErrors();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForFullGCComplete();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
if (_exceptions.Any())
|
||||
{
|
||||
throw new Exception($"{_exceptions.Count} exceptions tracked.\r\n" + string.Join(Environment.NewLine, _exceptions));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
TaskScheduler.UnobservedTaskException -= TrackUnobservedTaskException;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<MqttServer> StartServer()
|
||||
{
|
||||
return StartServer(ServerFactory.CreateServerOptionsBuilder());
|
||||
}
|
||||
|
||||
public async Task<MqttServer> StartServer(MqttServerOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.WithDefaultEndpoint();
|
||||
optionsBuilder.WithDefaultEndpointPort(ServerPort);
|
||||
optionsBuilder.WithMaxPendingMessagesPerClient(int.MaxValue);
|
||||
|
||||
var options = optionsBuilder.Build();
|
||||
var server = CreateServer(options);
|
||||
await server.StartAsync().ConfigureAwait(false);
|
||||
|
||||
// The OS has chosen the port to we have to properly expose it to the tests.
|
||||
ServerPort = options.DefaultEndpointOptions.Port;
|
||||
return server;
|
||||
}
|
||||
|
||||
public async Task<MqttServer> StartServer(Action<MqttServerOptionsBuilder> configure)
|
||||
{
|
||||
var optionsBuilder = ServerFactory.CreateServerOptionsBuilder();
|
||||
|
||||
optionsBuilder.WithDefaultEndpoint();
|
||||
optionsBuilder.WithDefaultEndpointPort(ServerPort);
|
||||
optionsBuilder.WithMaxPendingMessagesPerClient(int.MaxValue);
|
||||
|
||||
configure?.Invoke(optionsBuilder);
|
||||
|
||||
var options = optionsBuilder.Build();
|
||||
var server = CreateServer(options);
|
||||
await server.StartAsync().ConfigureAwait(false);
|
||||
|
||||
// The OS has chosen the port to we have to properly expose it to the tests.
|
||||
ServerPort = options.DefaultEndpointOptions.Port;
|
||||
return server;
|
||||
}
|
||||
|
||||
public void ThrowIfLogErrors()
|
||||
{
|
||||
if (!IgnoreServerLogErrors)
|
||||
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
||||
{
|
||||
lock (_serverErrors)
|
||||
{
|
||||
if (_serverErrors.Count > 0)
|
||||
{
|
||||
var message = $"Server had {_serverErrors.Count} errors (${string.Join(Environment.NewLine, _serverErrors)}).";
|
||||
Console.WriteLine(message);
|
||||
throw new Exception(message);
|
||||
}
|
||||
_serverErrors.Add(e.LogMessage.ToString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!IgnoreClientLogErrors)
|
||||
ClientLogger.LogMessagePublished += (_, e) =>
|
||||
{
|
||||
Debug.WriteLine(e.LogMessage.ToString());
|
||||
|
||||
if (e.LogMessage.Level == MqttNetLogLevel.Error)
|
||||
{
|
||||
lock (_clientErrors)
|
||||
if (!IgnoreClientLogErrors)
|
||||
{
|
||||
if (_clientErrors.Count > 0)
|
||||
lock (_clientErrors)
|
||||
{
|
||||
var message = $"Client(s) had {_clientErrors.Count} errors (${string.Join(Environment.NewLine, _clientErrors)})";
|
||||
Console.WriteLine(message);
|
||||
throw new Exception(message);
|
||||
_clientErrors.Add(e.LogMessage.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public MqttNetEventLogger ClientLogger { get; } = new("client");
|
||||
|
||||
public static bool EnableLogger { get; set; } = true;
|
||||
|
||||
public MqttClientFactory ClientFactory { get; } = new();
|
||||
|
||||
public MqttServerFactory ServerFactory { get; } = new();
|
||||
|
||||
public bool IgnoreClientLogErrors { get; set; }
|
||||
|
||||
public bool IgnoreServerLogErrors { get; set; }
|
||||
|
||||
public MqttServer Server { get; private set; }
|
||||
|
||||
public MqttNetEventLogger ServerLogger { get; } = new("server");
|
||||
|
||||
public int ServerPort { get; set; }
|
||||
|
||||
public TestContext TestContext { get; }
|
||||
|
||||
public Task<IMqttClient> ConnectClient()
|
||||
{
|
||||
return ConnectClient(ClientFactory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion));
|
||||
}
|
||||
|
||||
public async Task<IMqttClient> ConnectClient(Action<MqttClientOptionsBuilder> configureOptions, TimeSpan timeout = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(configureOptions);
|
||||
|
||||
// Start with initial default values.
|
||||
var optionsBuilder = ClientFactory.CreateClientOptionsBuilder().WithProtocolVersion(_protocolVersion).WithTcpServer("127.0.0.1", ServerPort);
|
||||
|
||||
// Let the caller override settings. Do not touch the options after this.
|
||||
configureOptions.Invoke(optionsBuilder);
|
||||
|
||||
var options = optionsBuilder.Build();
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var timeoutToken = new CancellationTokenSource(timeout);
|
||||
await client.ConnectAsync(options, timeoutToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void TrackException(Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
lock (_exceptions)
|
||||
{
|
||||
_exceptions.Add(exception);
|
||||
}
|
||||
public async Task<IMqttClient> ConnectClient(MqttClientOptionsBuilder options, TimeSpan timeout = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
options = options.WithTcpServer("127.0.0.1", ServerPort);
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
await client.ConnectAsync(options.Build()).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var timeoutToken = new CancellationTokenSource(timeout);
|
||||
await client.ConnectAsync(options.Build(), timeoutToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
void TrackUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||||
return client;
|
||||
}
|
||||
|
||||
public async Task<IMqttClient> ConnectClient(MqttClientOptions options, TimeSpan timeout = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
if (timeout == TimeSpan.Zero)
|
||||
{
|
||||
TrackException(e.Exception);
|
||||
await client.ConnectAsync(options).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var timeoutToken = new CancellationTokenSource(timeout);
|
||||
await client.ConnectAsync(options, timeoutToken.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public async Task<ILowLevelMqttClient> ConnectLowLevelClient(Action<MqttClientOptionsBuilder> optionsBuilder = null)
|
||||
{
|
||||
var options = new MqttClientOptionsBuilder();
|
||||
options = options.WithTcpServer("127.0.0.1", ServerPort);
|
||||
optionsBuilder?.Invoke(options);
|
||||
|
||||
var client = CreateLowLevelClient();
|
||||
await client.ConnectAsync(options.Build(), CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public async Task<MqttRpcClient> ConnectRpcClient(MqttRpcClientOptions options)
|
||||
{
|
||||
return new MqttRpcClient(await ConnectClient(), options);
|
||||
}
|
||||
|
||||
public TestApplicationMessageReceivedHandler CreateApplicationMessageHandler(IMqttClient mqttClient)
|
||||
{
|
||||
return new TestApplicationMessageReceivedHandler(mqttClient);
|
||||
}
|
||||
|
||||
public IMqttClient CreateClient()
|
||||
{
|
||||
var logger = EnableLogger ? (IMqttNetLogger)ClientLogger : MqttNetNullLogger.Instance;
|
||||
|
||||
var client = ClientFactory.CreateMqttClient(logger);
|
||||
|
||||
client.ConnectingAsync += e =>
|
||||
{
|
||||
if (TestContext != null)
|
||||
{
|
||||
var clientOptions = e.ClientOptions;
|
||||
var existingClientId = clientOptions.ClientId;
|
||||
if (existingClientId != null && !existingClientId.StartsWith(TestContext.TestName))
|
||||
{
|
||||
clientOptions.ClientId = TestContext.TestName + "_" + existingClientId;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
lock (_clients)
|
||||
{
|
||||
_clients.Add(client);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public MqttClientOptions CreateDefaultClientOptions()
|
||||
{
|
||||
return CreateDefaultClientOptionsBuilder().Build();
|
||||
}
|
||||
|
||||
public MqttClientOptionsBuilder CreateDefaultClientOptionsBuilder()
|
||||
{
|
||||
return ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(_protocolVersion)
|
||||
.WithTcpServer("127.0.0.1", ServerPort)
|
||||
.WithClientId(TestContext.TestName + "_" + Guid.NewGuid());
|
||||
}
|
||||
|
||||
public ILowLevelMqttClient CreateLowLevelClient()
|
||||
{
|
||||
var client = ClientFactory.CreateLowLevelMqttClient(ClientLogger);
|
||||
|
||||
lock (_lowLevelClients)
|
||||
{
|
||||
_lowLevelClients.Add(client);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
public MqttServer CreateServer(MqttServerOptions options)
|
||||
{
|
||||
if (Server != null)
|
||||
{
|
||||
throw new InvalidOperationException("Server already started.");
|
||||
}
|
||||
|
||||
var logger = EnableLogger ? (IMqttNetLogger)ServerLogger : new MqttNetNullLogger();
|
||||
|
||||
Server = ServerFactory.CreateMqttServer(options, logger);
|
||||
|
||||
Server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (TestContext != null)
|
||||
{
|
||||
// Null is used when the client id is assigned from the server!
|
||||
if (!string.IsNullOrEmpty(e.ClientId) && !e.ClientId.StartsWith(TestContext.TestName))
|
||||
{
|
||||
TrackException(new InvalidOperationException($"Invalid client ID used ({e.ClientId}). It must start with UnitTest name."));
|
||||
e.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
return Server;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_clients)
|
||||
{
|
||||
foreach (var mqttClient in _clients)
|
||||
{
|
||||
try
|
||||
{
|
||||
//mqttClient.DisconnectAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// This can happen when the test already disconnected the client.
|
||||
}
|
||||
finally
|
||||
{
|
||||
mqttClient?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
_clients.Clear();
|
||||
}
|
||||
|
||||
lock (_lowLevelClients)
|
||||
{
|
||||
foreach (var lowLevelMqttClient in _lowLevelClients)
|
||||
{
|
||||
lowLevelMqttClient.Dispose();
|
||||
}
|
||||
|
||||
_lowLevelClients.Clear();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Server?.StopAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// This can happen when the test already stopped the server.
|
||||
}
|
||||
finally
|
||||
{
|
||||
Server?.Dispose();
|
||||
}
|
||||
|
||||
Server = null;
|
||||
|
||||
ThrowIfLogErrors();
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForFullGCComplete();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
if (_exceptions.Any())
|
||||
{
|
||||
// ReSharper disable once ThrowExceptionInUnexpectedLocation
|
||||
throw new Exception($"{_exceptions.Count} exceptions tracked.\r\n" + string.Join(Environment.NewLine, _exceptions));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
TaskScheduler.UnobservedTaskException -= TrackUnobservedTaskException;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<MqttServer> StartServer()
|
||||
{
|
||||
return StartServer(ServerFactory.CreateServerOptionsBuilder());
|
||||
}
|
||||
|
||||
public async Task<MqttServer> StartServer(MqttServerOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.WithDefaultEndpoint();
|
||||
optionsBuilder.WithDefaultEndpointPort(ServerPort);
|
||||
optionsBuilder.WithMaxPendingMessagesPerClient(int.MaxValue);
|
||||
|
||||
var options = optionsBuilder.Build();
|
||||
var server = CreateServer(options);
|
||||
await server.StartAsync().ConfigureAwait(false);
|
||||
|
||||
// The OS has chosen the port to we have to properly expose it to the tests.
|
||||
ServerPort = options.DefaultEndpointOptions.Port;
|
||||
return server;
|
||||
}
|
||||
|
||||
public async Task<MqttServer> StartServer(Action<MqttServerOptionsBuilder> configure)
|
||||
{
|
||||
var optionsBuilder = ServerFactory.CreateServerOptionsBuilder();
|
||||
|
||||
optionsBuilder.WithDefaultEndpoint();
|
||||
optionsBuilder.WithDefaultEndpointPort(ServerPort);
|
||||
optionsBuilder.WithMaxPendingMessagesPerClient(int.MaxValue);
|
||||
|
||||
configure?.Invoke(optionsBuilder);
|
||||
|
||||
var options = optionsBuilder.Build();
|
||||
var server = CreateServer(options);
|
||||
await server.StartAsync().ConfigureAwait(false);
|
||||
|
||||
// The OS has chosen the port to we have to properly expose it to the tests.
|
||||
ServerPort = options.DefaultEndpointOptions.Port;
|
||||
return server;
|
||||
}
|
||||
|
||||
public void ThrowIfLogErrors()
|
||||
{
|
||||
if (!IgnoreServerLogErrors)
|
||||
{
|
||||
lock (_serverErrors)
|
||||
{
|
||||
if (_serverErrors.Count > 0)
|
||||
{
|
||||
var message = $"Server had {_serverErrors.Count} errors (${string.Join(Environment.NewLine, _serverErrors)}).";
|
||||
Console.WriteLine(message);
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!IgnoreClientLogErrors)
|
||||
{
|
||||
lock (_clientErrors)
|
||||
{
|
||||
if (_clientErrors.Count > 0)
|
||||
{
|
||||
var message = $"Client(s) had {_clientErrors.Count} errors (${string.Join(Environment.NewLine, _clientErrors)})";
|
||||
Console.WriteLine(message);
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TrackException(Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_exceptions)
|
||||
{
|
||||
_exceptions.Add(exception);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||||
{
|
||||
TrackException(e.Exception);
|
||||
}
|
||||
}
|
||||
@@ -5,22 +5,21 @@
|
||||
using System;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Tests.Mockups
|
||||
namespace MQTTnet.Tests.Mockups;
|
||||
|
||||
public sealed class TestLogger : IMqttNetLogger
|
||||
{
|
||||
public sealed class TestLogger : IMqttNetLogger
|
||||
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
||||
|
||||
public bool IsEnabled { get; } = true;
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||
{
|
||||
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
||||
|
||||
public bool IsEnabled { get; } = true;
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||
LogMessagePublished?.Invoke(this, new MqttNetLogMessagePublishedEventArgs(new MqttNetLogMessage
|
||||
{
|
||||
LogMessagePublished?.Invoke(this, new MqttNetLogMessagePublishedEventArgs(new MqttNetLogMessage
|
||||
{
|
||||
Level = logLevel,
|
||||
Message = string.Format(message, parameters),
|
||||
Exception = exception
|
||||
}));
|
||||
}
|
||||
Level = logLevel,
|
||||
Message = string.Format(message, parameters),
|
||||
Exception = exception
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,58 +8,58 @@ using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttApplicationMessageBuilder_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttApplicationMessageBuilder_Tests
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_TopicOnly()
|
||||
{
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_TopicOnly()
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("Abc").Build();
|
||||
Assert.AreEqual("Abc", message.Topic);
|
||||
Assert.IsFalse(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_TimeStampPayload()
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("xyz").WithPayload(TimeSpan.FromSeconds(360).ToString()).Build();
|
||||
Assert.AreEqual("xyz", message.Topic);
|
||||
Assert.IsFalse(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(message.Payload), "00:06:00");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_StreamPayload()
|
||||
{
|
||||
var stream = new MemoryStream(Encoding.UTF8.GetBytes("xHello")) { Position = 1 };
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("123").WithPayload(stream).Build();
|
||||
Assert.AreEqual("123", message.Topic);
|
||||
Assert.IsFalse(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(message.Payload), "Hello");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_Retained()
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("lol").WithRetainFlag().Build();
|
||||
Assert.AreEqual("lol", message.Topic);
|
||||
Assert.IsTrue(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_QosLevel2()
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("rofl").WithRetainFlag().WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce).Build();
|
||||
Assert.AreEqual("rofl", message.Topic);
|
||||
Assert.IsTrue(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.ExactlyOnce, message.QualityOfServiceLevel);
|
||||
}
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("Abc").Build();
|
||||
Assert.AreEqual("Abc", message.Topic);
|
||||
Assert.IsFalse(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_TimeStampPayload()
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("xyz").WithPayload(TimeSpan.FromSeconds(360).ToString()).Build();
|
||||
Assert.AreEqual("xyz", message.Topic);
|
||||
Assert.IsFalse(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(message.Payload), "00:06:00");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_StreamPayload()
|
||||
{
|
||||
var stream = new MemoryStream("xHello"u8.ToArray()) { Position = 1 };
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("123").WithPayload(stream).Build();
|
||||
Assert.AreEqual("123", message.Topic);
|
||||
Assert.IsFalse(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
Assert.AreEqual(Encoding.UTF8.GetString(message.Payload), "Hello");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_Retained()
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("lol").WithRetainFlag().Build();
|
||||
Assert.AreEqual("lol", message.Topic);
|
||||
Assert.IsTrue(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtMostOnce, message.QualityOfServiceLevel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CreateApplicationMessage_QosLevel2()
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("rofl").WithRetainFlag().WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce).Build();
|
||||
Assert.AreEqual("rofl", message.Topic);
|
||||
Assert.IsTrue(message.Retain);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.ExactlyOnce, message.QualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
@@ -6,39 +6,39 @@ using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttApplicationMessageValidator_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttApplicationMessageValidator_Tests
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Succeed_When_Using_TopicAlias_And_MQTT_311()
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Succeed_When_Using_TopicAlias_And_MQTT_311()
|
||||
{
|
||||
MqttApplicationMessageValidator.ThrowIfNotSupported(new MqttApplicationMessageBuilder().WithTopicAlias(1).Build(), MqttProtocolVersion.V311);
|
||||
}
|
||||
MqttApplicationMessageValidator.ThrowIfNotSupported(new MqttApplicationMessageBuilder().WithTopicAlias(1).Build(), MqttProtocolVersion.V311);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Succeed_When_Using_TopicAlias_And_MQTT_500()
|
||||
{
|
||||
MqttApplicationMessageValidator.ThrowIfNotSupported(new MqttApplicationMessageBuilder().WithTopicAlias(1).Build(), MqttProtocolVersion.V500);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Succeed_When_Using_TopicAlias_And_MQTT_500()
|
||||
{
|
||||
MqttApplicationMessageValidator.ThrowIfNotSupported(new MqttApplicationMessageBuilder().WithTopicAlias(1).Build(), MqttProtocolVersion.V500);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Succeed_When_Using_UserProperties_And_MQTT_500()
|
||||
{
|
||||
MqttApplicationMessageValidator.ThrowIfNotSupported(
|
||||
new MqttApplicationMessageBuilder().WithTopic("A").WithUserProperty("User", "Property").Build(),
|
||||
MqttProtocolVersion.V500);
|
||||
}
|
||||
[TestMethod]
|
||||
public void Succeed_When_Using_UserProperties_And_MQTT_500()
|
||||
{
|
||||
MqttApplicationMessageValidator.ThrowIfNotSupported(
|
||||
new MqttApplicationMessageBuilder().WithTopic("A").WithUserProperty("User", "Property").Build(),
|
||||
MqttProtocolVersion.V500);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Succeed_When_Using_WillUserProperties_And_MQTT_311()
|
||||
{
|
||||
MqttApplicationMessageValidator.ThrowIfNotSupported(
|
||||
new MqttApplicationMessageBuilder().WithTopic("B").WithUserProperty("User", "Property").Build(),
|
||||
MqttProtocolVersion.V311);
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Succeed_When_Using_WillUserProperties_And_MQTT_311()
|
||||
{
|
||||
MqttApplicationMessageValidator.ThrowIfNotSupported(
|
||||
new MqttApplicationMessageBuilder().WithTopic("B").WithUserProperty("User", "Property").Build(),
|
||||
MqttProtocolVersion.V311);
|
||||
}
|
||||
}
|
||||
@@ -6,48 +6,48 @@ using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttClientOptionsValidator_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttClientOptionsValidator_Tests
|
||||
[TestMethod]
|
||||
public void Succeed_When_Using_UserProperties_And_MQTT_500()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Succeed_When_Using_UserProperties_And_MQTT_500()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithUserProperty("User", "Property").WithTcpServer("FAKE").Build();
|
||||
}
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithUserProperty("User", "Property").WithTcpServer("FAKE").Build();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Succeed_When_Using_WillUserProperties_And_MQTT_311()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V311).WithWillUserProperty("User", "Property").WithTcpServer("FAKE").Build();
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Succeed_When_Using_WillUserProperties_And_MQTT_311()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V311).WithWillUserProperty("User", "Property").WithTcpServer("FAKE").Build();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Succeed_When_Using_WillUserProperties_And_MQTT_500()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithWillUserProperty("User", "Property").WithTcpServer("FAKE").Build();
|
||||
}
|
||||
[TestMethod]
|
||||
public void Succeed_When_Using_WillUserProperties_And_MQTT_500()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithWillUserProperty("User", "Property").WithTcpServer("FAKE").Build();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Throw_When_Using_UserProperties_And_MQTT_311()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V311).WithUserProperty("User", "Property").WithTcpServer("FAKE").Build();
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Throw_When_Using_UserProperties_And_MQTT_311()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V311).WithUserProperty("User", "Property").WithTcpServer("FAKE").Build();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Throw_When_Using_WithRequestResponseInformation_And_MQTT_311()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V311).WithRequestResponseInformation().WithTcpServer("FAKE").Build();
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotSupportedException))]
|
||||
public void Throw_When_Using_WithRequestResponseInformation_And_MQTT_311()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V311).WithRequestResponseInformation().WithTcpServer("FAKE").Build();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Throw_When_Using_WithRequestResponseInformation_And_MQTT_500()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithRequestResponseInformation().WithTcpServer("FAKE").Build();
|
||||
}
|
||||
[TestMethod]
|
||||
public void Throw_When_Using_WithRequestResponseInformation_And_MQTT_500()
|
||||
{
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithRequestResponseInformation().WithTcpServer("FAKE").Build();
|
||||
}
|
||||
}
|
||||
@@ -4,32 +4,32 @@
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class MqttPacketIdentifierProvider_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttPacketIdentifierProvider_Tests
|
||||
[TestMethod]
|
||||
public void Reset()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Reset()
|
||||
{
|
||||
var p = new MqttPacketIdentifierProvider();
|
||||
Assert.AreEqual(1, p.GetNextPacketIdentifier());
|
||||
Assert.AreEqual(2, p.GetNextPacketIdentifier());
|
||||
p.Reset();
|
||||
Assert.AreEqual(1, p.GetNextPacketIdentifier());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReachBoundaries()
|
||||
{
|
||||
var p = new MqttPacketIdentifierProvider();
|
||||
|
||||
for (ushort i = 0; i < ushort.MaxValue; i++)
|
||||
{
|
||||
Assert.AreEqual(i + 1, p.GetNextPacketIdentifier());
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, p.GetNextPacketIdentifier());
|
||||
}
|
||||
var p = new MqttPacketIdentifierProvider();
|
||||
Assert.AreEqual(1, p.GetNextPacketIdentifier());
|
||||
Assert.AreEqual(2, p.GetNextPacketIdentifier());
|
||||
p.Reset();
|
||||
Assert.AreEqual(1, p.GetNextPacketIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReachBoundaries()
|
||||
{
|
||||
var p = new MqttPacketIdentifierProvider();
|
||||
|
||||
for (ushort i = 0; i < ushort.MaxValue; i++)
|
||||
{
|
||||
Assert.AreEqual(i + 1, p.GetNextPacketIdentifier());
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, p.GetNextPacketIdentifier());
|
||||
}
|
||||
}
|
||||
@@ -5,30 +5,30 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class MqttPacketWriter_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttPacketWriter_Tests
|
||||
protected virtual MqttBufferWriter WriterFactory()
|
||||
{
|
||||
protected virtual MqttBufferWriter WriterFactory()
|
||||
{
|
||||
return new MqttBufferWriter(4096, 65535);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WritePacket()
|
||||
{
|
||||
var writer = WriterFactory();
|
||||
Assert.AreEqual(0, writer.Length);
|
||||
|
||||
writer.WriteString("1234567890");
|
||||
Assert.AreEqual(10 + 2, writer.Length);
|
||||
|
||||
writer.WriteBinary(new byte[300]);
|
||||
Assert.AreEqual(300 + 2 + 12, writer.Length);
|
||||
|
||||
writer.WriteBinary(new byte[5000]);
|
||||
Assert.AreEqual(5000 + 2 + 300 + 2 + 12, writer.Length);
|
||||
}
|
||||
return new MqttBufferWriter(4096, 65535);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WritePacket()
|
||||
{
|
||||
var writer = WriterFactory();
|
||||
Assert.AreEqual(0, writer.Length);
|
||||
|
||||
writer.WriteString("1234567890");
|
||||
Assert.AreEqual(10 + 2, writer.Length);
|
||||
|
||||
writer.WriteBinary(new byte[300]);
|
||||
Assert.AreEqual(300 + 2 + 12, writer.Length);
|
||||
|
||||
writer.WriteBinary(new byte[5000]);
|
||||
Assert.AreEqual(5000 + 2 + 300 + 2 + 12, writer.Length);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class MqttTcpChannel_Tests
|
||||
{
|
||||
|
||||
@@ -6,53 +6,53 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class MqttTopicValidatorSubscribe_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class MqttTopicValidatorSubscribe_Tests
|
||||
[TestMethod]
|
||||
public void Valid_Topic()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Valid_Topic()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/b/c");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic_Plus_In_Between()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/+/c");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic_Plus_Last_Char()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/+");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic_Hash_Last_Char()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/#");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic_Only_Hash()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("#");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Hash_In_Between()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/#/c");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Empty()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe(string.Empty);
|
||||
}
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/b/c");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic_Plus_In_Between()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/+/c");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic_Plus_Last_Char()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/+");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic_Hash_Last_Char()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/#");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic_Only_Hash()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("#");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Hash_In_Between()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe("/a/#/c");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Empty()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalidSubscribe(string.Empty);
|
||||
}
|
||||
}
|
||||
@@ -6,36 +6,36 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttTopicValidator_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttTopicValidator_Tests
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Empty()
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Empty()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalid(string.Empty);
|
||||
}
|
||||
MqttTopicValidator.ThrowIfInvalid(string.Empty);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Hash()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalid("/a/#/c");
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Hash()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalid("/a/#/c");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Plus()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalid("/a/+/c");
|
||||
}
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttProtocolViolationException))]
|
||||
public void Invalid_Topic_Plus()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalid("/a/+/c");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Valid_Topic()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalid("/a/b/c");
|
||||
}
|
||||
[TestMethod]
|
||||
public void Valid_Topic()
|
||||
{
|
||||
MqttTopicValidator.ThrowIfInvalid("/a/b/c");
|
||||
}
|
||||
}
|
||||
@@ -5,51 +5,51 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Protocol_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Protocol_Tests
|
||||
[TestMethod]
|
||||
public void Encode_Four_Byte_Integer()
|
||||
{
|
||||
[TestMethod]
|
||||
public void Encode_Four_Byte_Integer()
|
||||
var writer = new MqttBufferWriter(4, 4);
|
||||
|
||||
for (uint value = 0; value < 268435455; value++)
|
||||
{
|
||||
var writer = new MqttBufferWriter(4, 4);
|
||||
writer.WriteVariableByteInteger(value);
|
||||
|
||||
for (uint value = 0; value < 268435455; value++)
|
||||
{
|
||||
writer.WriteVariableByteInteger(value);
|
||||
var buffer = writer.GetBuffer();
|
||||
|
||||
var buffer = writer.GetBuffer();
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(buffer, 0, writer.Length);
|
||||
var checkValue = reader.ReadVariableByteInteger();
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(buffer, 0, writer.Length);
|
||||
var checkValue = reader.ReadVariableByteInteger();
|
||||
Assert.AreEqual(value, checkValue);
|
||||
|
||||
Assert.AreEqual(value, checkValue);
|
||||
|
||||
writer.Reset(0);
|
||||
}
|
||||
writer.Reset(0);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Encode_Two_Byte_Integer()
|
||||
[TestMethod]
|
||||
public void Encode_Two_Byte_Integer()
|
||||
{
|
||||
var writer = new MqttBufferWriter(2, 2);
|
||||
|
||||
for (ushort value = 0; value < ushort.MaxValue; value++)
|
||||
{
|
||||
var writer = new MqttBufferWriter(2, 2);
|
||||
writer.WriteTwoByteInteger(value);
|
||||
|
||||
for (ushort value = 0; value < ushort.MaxValue; value++)
|
||||
{
|
||||
writer.WriteTwoByteInteger(value);
|
||||
var buffer = writer.GetBuffer();
|
||||
|
||||
var buffer = writer.GetBuffer();
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(buffer, 0, writer.Length);
|
||||
var checkValue = reader.ReadTwoByteInteger();
|
||||
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(buffer, 0, writer.Length);
|
||||
var checkValue = reader.ReadTwoByteInteger();
|
||||
Assert.AreEqual(value, checkValue);
|
||||
|
||||
Assert.AreEqual(value, checkValue);
|
||||
|
||||
writer.Reset(0);
|
||||
}
|
||||
writer.Reset(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,52 +10,50 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class RoundtripTime_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class RoundtripTime_Tests
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
[TestMethod]
|
||||
public async Task Round_Trip_Time()
|
||||
{
|
||||
public TestContext TestContext { get; set; }
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Round_Trip_Time()
|
||||
var receiverClient = await testEnvironment.ConnectClient();
|
||||
var senderClient = await testEnvironment.ConnectClient();
|
||||
|
||||
TaskCompletionSource<string> response = null;
|
||||
|
||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
response?.TrySetResult(e.ApplicationMessage.ConvertPayloadToString());
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await receiverClient.SubscribeAsync("#");
|
||||
|
||||
var times = new List<TimeSpan>();
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
response = new TaskCompletionSource<string>();
|
||||
await senderClient.PublishStringAsync("test", DateTime.UtcNow.Ticks.ToString());
|
||||
if (!response.Task.Wait(TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receiverClient = await testEnvironment.ConnectClient();
|
||||
var senderClient = await testEnvironment.ConnectClient();
|
||||
|
||||
TaskCompletionSource<string> response = null;
|
||||
|
||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
response?.TrySetResult(e.ApplicationMessage.ConvertPayloadToString());
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await receiverClient.SubscribeAsync("#");
|
||||
|
||||
var times = new List<TimeSpan>();
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
response = new TaskCompletionSource<string>();
|
||||
await senderClient.PublishStringAsync("test", DateTime.UtcNow.Ticks.ToString());
|
||||
if (!response.Task.Wait(TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
times.Add(stopwatch.Elapsed);
|
||||
stopwatch.Restart();
|
||||
}
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
times.Add(stopwatch.Elapsed);
|
||||
stopwatch.Restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,84 +9,82 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Assigned_Client_ID_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Assigned_Client_ID_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public Task Connect_With_No_Client_Id()
|
||||
{
|
||||
[TestMethod]
|
||||
public Task Connect_With_No_Client_Id()
|
||||
{
|
||||
return Connect_With_Client_Id("test_123", "test_123", null, "test_123");
|
||||
}
|
||||
return Connect_With_Client_Id("test_123", "test_123", null, "test_123");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Connect_With_Client_Id()
|
||||
{
|
||||
return Connect_With_Client_Id("Connect_With_Client_Id_test_456", null, "test_456", null);
|
||||
}
|
||||
[TestMethod]
|
||||
public Task Connect_With_Client_Id()
|
||||
{
|
||||
return Connect_With_Client_Id("Connect_With_Client_Id_test_456", null, "test_456", null);
|
||||
}
|
||||
|
||||
async Task Connect_With_Client_Id(string expectedClientId, string expectedReturnedClientId, string usedClientId, string assignedClientId)
|
||||
async Task Connect_With_Client_Id(string expectedClientId, string expectedReturnedClientId, string usedClientId, string assignedClientId)
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
string serverConnectedClientId = null;
|
||||
string serverDisconnectedClientId = null;
|
||||
string clientAssignedClientId = null;
|
||||
|
||||
// Arrange server
|
||||
var disconnectedMre = new ManualResetEventSlim();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
if (string.IsNullOrEmpty(e.ClientId))
|
||||
{
|
||||
string serverConnectedClientId = null;
|
||||
string serverDisconnectedClientId = null;
|
||||
string clientAssignedClientId = null;
|
||||
|
||||
// Arrange server
|
||||
var disconnectedMre = new ManualResetEventSlim();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(e.ClientId))
|
||||
{
|
||||
e.AssignedClientIdentifier = assignedClientId;
|
||||
e.ReasonCode = MqttConnectReasonCode.Success;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ClientConnectedAsync += args =>
|
||||
{
|
||||
serverConnectedClientId = args.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
serverDisconnectedClientId = args.ClientId;
|
||||
disconnectedMre.Set();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
// Arrange client
|
||||
var client = testEnvironment.CreateClient();
|
||||
client.ConnectedAsync += args =>
|
||||
{
|
||||
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
// Act
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithClientId(usedClientId)
|
||||
.Build());
|
||||
|
||||
await client.DisconnectAsync();
|
||||
|
||||
// Wait for ClientDisconnectedHandler to trigger
|
||||
disconnectedMre.Wait(1000);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(expectedClientId, serverConnectedClientId);
|
||||
Assert.AreEqual(expectedClientId, serverDisconnectedClientId);
|
||||
Assert.AreEqual(expectedReturnedClientId, clientAssignedClientId);
|
||||
e.AssignedClientIdentifier = assignedClientId;
|
||||
e.ReasonCode = MqttConnectReasonCode.Success;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ClientConnectedAsync += args =>
|
||||
{
|
||||
serverConnectedClientId = args.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
serverDisconnectedClientId = args.ClientId;
|
||||
disconnectedMre.Set();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
// Arrange client
|
||||
var client = testEnvironment.CreateClient();
|
||||
client.ConnectedAsync += args =>
|
||||
{
|
||||
clientAssignedClientId = args.ConnectResult.AssignedClientIdentifier;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
// Act
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithClientId(usedClientId)
|
||||
.Build());
|
||||
|
||||
await client.DisconnectAsync();
|
||||
|
||||
// Wait for ClientDisconnectedHandler to trigger
|
||||
disconnectedMre.Wait(1000);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(expectedClientId, serverConnectedClientId);
|
||||
Assert.AreEqual(expectedClientId, serverDisconnectedClientId);
|
||||
Assert.AreEqual(expectedReturnedClientId, clientAssignedClientId);
|
||||
}
|
||||
}
|
||||
@@ -5,78 +5,73 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Connection_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Connection_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Close_Idle_Connection_On_Connect()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Close_Idle_Connection_On_Connect()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
||||
|
||||
var client = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
|
||||
await client.ConnectAsync(new DnsEndPoint("localhost", testEnvironment.ServerPort), CancellationToken.None);
|
||||
|
||||
// Don't send anything. The server should close the connection.
|
||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||
|
||||
try
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
||||
if (receivedBytes == 0)
|
||||
{
|
||||
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
||||
|
||||
var client = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
|
||||
await client.ConnectAsync(new DnsEndPoint("localhost", testEnvironment.ServerPort), CancellationToken.None);
|
||||
|
||||
// Don't send anything. The server should close the connection.
|
||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||
|
||||
try
|
||||
{
|
||||
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
||||
if (receivedBytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Fail("Receive should throw an exception.");
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Fail("Receive should throw an exception.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Send_Garbage()
|
||||
catch (SocketException)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Send_Garbage()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
||||
|
||||
// Send an invalid packet and ensure that the server will close the connection and stay in a waiting state
|
||||
// forever. This is security related.
|
||||
var client = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
|
||||
await client.ConnectAsync(new DnsEndPoint("localhost", testEnvironment.ServerPort), CancellationToken.None);
|
||||
|
||||
var buffer = "Garbage"u8.ToArray();
|
||||
await client.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||
|
||||
try
|
||||
{
|
||||
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
||||
if (receivedBytes == 0)
|
||||
{
|
||||
await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(1)));
|
||||
|
||||
// Send an invalid packet and ensure that the server will close the connection and stay in a waiting state
|
||||
// forever. This is security related.
|
||||
var client = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
|
||||
await client.ConnectAsync(new DnsEndPoint("localhost", testEnvironment.ServerPort), CancellationToken.None);
|
||||
|
||||
var buffer = Encoding.UTF8.GetBytes("Garbage");
|
||||
await client.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(3));
|
||||
|
||||
try
|
||||
{
|
||||
var receivedBytes = await client.ReceiveAsync(new ArraySegment<byte>(new byte[10]), SocketFlags.Partial);
|
||||
if (receivedBytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Fail("Receive should throw an exception.");
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Fail("Receive should throw an exception.");
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,63 +4,59 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Cross_Version_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Cross_Version_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Send_V311_Receive_V500()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Send_V311_Receive_V500()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var receivedApplicationMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
await receiver.SubscribeAsync("#");
|
||||
var receiver = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var receivedApplicationMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message").WithPayload("My_Payload").Build();
|
||||
await sender.PublishAsync(applicationMessage);
|
||||
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message").WithPayload("My_Payload").Build();
|
||||
await sender.PublishAsync(applicationMessage);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, receivedApplicationMessages.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("My/Message", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.Topic);
|
||||
Assert.AreEqual("My_Payload", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.ConvertPayloadToString());
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(1, receivedApplicationMessages.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("My/Message", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.Topic);
|
||||
Assert.AreEqual("My_Payload", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.ConvertPayloadToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Send_V500_Receive_V311()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
[TestMethod]
|
||||
public async Task Send_V500_Receive_V311()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V311));
|
||||
var receivedApplicationMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
await receiver.SubscribeAsync("#");
|
||||
var receiver = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V311));
|
||||
var receivedApplicationMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message")
|
||||
.WithPayload("My_Payload")
|
||||
.WithUserProperty("A", "B")
|
||||
.WithResponseTopic("Response")
|
||||
.WithCorrelationData(Encoding.UTF8.GetBytes("Correlation"))
|
||||
.Build();
|
||||
var applicationMessage = new MqttApplicationMessageBuilder().WithTopic("My/Message")
|
||||
.WithPayload("My_Payload")
|
||||
.WithUserProperty("A", "B")
|
||||
.WithResponseTopic("Response")
|
||||
.WithCorrelationData(Encoding.UTF8.GetBytes("Correlation"))
|
||||
.Build();
|
||||
|
||||
await sender.PublishAsync(applicationMessage);
|
||||
await sender.PublishAsync(applicationMessage);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, receivedApplicationMessages.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("My/Message", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.Topic);
|
||||
Assert.AreEqual("My_Payload", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.ConvertPayloadToString());
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(1, receivedApplicationMessages.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("My/Message", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.Topic);
|
||||
Assert.AreEqual("My_Payload", receivedApplicationMessages.ReceivedEventArgs.First().ApplicationMessage.ConvertPayloadToString());
|
||||
}
|
||||
}
|
||||
@@ -10,194 +10,180 @@ using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Events_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Events_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Connected_Event()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Connected_Event()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientConnectedEventArgs eventArgs = null;
|
||||
server.ClientConnectedAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
ClientConnectedEventArgs eventArgs = null;
|
||||
server.ClientConnectedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
|
||||
|
||||
await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Connected_Event)));
|
||||
Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
|
||||
Assert.AreEqual(MqttProtocolVersion.V311, eventArgs.ProtocolVersion);
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
Assert.AreEqual("ThePassword", eventArgs.Password);
|
||||
}
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Connected_Event)));
|
||||
Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
|
||||
Assert.AreEqual(MqttProtocolVersion.V311, eventArgs.ProtocolVersion);
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
Assert.AreEqual("ThePassword", eventArgs.Password);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Disconnected_Event()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Disconnected_Event()
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
|
||||
await client.DisconnectAsync();
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser", "ThePassword"));
|
||||
await client.DisconnectAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Disconnected_Event)));
|
||||
Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
|
||||
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Disconnected_Event)));
|
||||
Assert.IsTrue(eventArgs.RemoteEndPoint.ToString().Contains("127.0.0.1"));
|
||||
Assert.AreEqual(MqttClientDisconnectType.Clean, eventArgs.DisconnectType);
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
Assert.AreEqual("ThePassword", eventArgs.Password);
|
||||
}
|
||||
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
Assert.AreEqual("ThePassword", eventArgs.Password);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Subscribed_Event()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Subscribed_Event()
|
||||
ClientSubscribedTopicEventArgs eventArgs = null;
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
ClientSubscribedTopicEventArgs eventArgs = null;
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
await client.SubscribeAsync("The/Topic", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
await client.SubscribeAsync("The/Topic", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Subscribed_Event)));
|
||||
Assert.AreEqual("The/Topic", eventArgs.TopicFilter.Topic);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, eventArgs.TopicFilter.QualityOfServiceLevel);
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
}
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Subscribed_Event)));
|
||||
Assert.AreEqual("The/Topic", eventArgs.TopicFilter.Topic);
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, eventArgs.TopicFilter.QualityOfServiceLevel);
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Unsubscribed_Event()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Client_Unsubscribed_Event()
|
||||
ClientUnsubscribedTopicEventArgs eventArgs = null;
|
||||
server.ClientUnsubscribedTopicAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
ClientUnsubscribedTopicEventArgs eventArgs = null;
|
||||
server.ClientUnsubscribedTopicAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
await client.UnsubscribeAsync("The/Topic");
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
await client.UnsubscribeAsync("The/Topic");
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Unsubscribed_Event)));
|
||||
Assert.AreEqual("The/Topic", eventArgs.TopicFilter);
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
}
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Client_Unsubscribed_Event)));
|
||||
Assert.AreEqual("The/Topic", eventArgs.TopicFilter);
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Fire_Application_Message_Received_Event()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Application_Message_Received_Event()
|
||||
InterceptingPublishEventArgs eventArgs = null;
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
InterceptingPublishEventArgs eventArgs = null;
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
await client.PublishStringAsync("The_Topic", "The_Payload");
|
||||
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithCredentials("TheUser"));
|
||||
await client.PublishStringAsync("The_Topic", "The_Payload");
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
Assert.IsNotNull(eventArgs);
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Application_Message_Received_Event)));
|
||||
Assert.AreEqual("The_Topic", eventArgs.ApplicationMessage.Topic);
|
||||
Assert.AreEqual("The_Payload", eventArgs.ApplicationMessage.ConvertPayloadToString());
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
}
|
||||
|
||||
Assert.IsTrue(eventArgs.ClientId.StartsWith(nameof(Fire_Application_Message_Received_Event)));
|
||||
Assert.AreEqual("The_Topic", eventArgs.ApplicationMessage.Topic);
|
||||
Assert.AreEqual("The_Payload", eventArgs.ApplicationMessage.ConvertPayloadToString());
|
||||
Assert.AreEqual("TheUser", eventArgs.UserName);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Fire_Started_Event()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = testEnvironment.CreateServer(new MqttServerOptions());
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Started_Event()
|
||||
EventArgs eventArgs = null;
|
||||
server.StartedAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = testEnvironment.CreateServer(new MqttServerOptions());
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
EventArgs eventArgs = null;
|
||||
server.StartedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
await server.StartAsync();
|
||||
|
||||
await server.StartAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
Assert.IsNotNull(eventArgs);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Fire_Stopped_Event()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Stopped_Event()
|
||||
EventArgs eventArgs = null;
|
||||
server.StoppedAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
EventArgs eventArgs = null;
|
||||
server.StoppedAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
await server.StopAsync();
|
||||
|
||||
await server.StopAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
}
|
||||
}
|
||||
Assert.IsNotNull(eventArgs);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,403 +15,390 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
// missing certificate builder api means tests won't work for older frameworks
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class HotSwapCerts_Tests
|
||||
{
|
||||
// missing certificate builder api means tests won't work for older frameworks
|
||||
static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
[TestClass]
|
||||
public sealed class HotSwapCerts_Tests
|
||||
[TestMethod]
|
||||
public async Task ClientCertChangeWithoutServerUpdateFailsReconnect()
|
||||
{
|
||||
static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
|
||||
using var server = new ServerTestHarness();
|
||||
using var client01 = new ClientTestHarness();
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
[TestMethod]
|
||||
public async Task ClientCertChangeWithoutServerUpdateFailsReconnect()
|
||||
await server.StartServer();
|
||||
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.HotSwapClientCert();
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.WaitForConnectToFail(DefaultTimeout);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ClientCertChangeWithServerUpdateAcceptsReconnect()
|
||||
{
|
||||
using var server = new ServerTestHarness();
|
||||
using var client01 = new ClientTestHarness();
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.HotSwapClientCert();
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ServerCertChangeWithClientCertUpdateAllowsReconnect()
|
||||
{
|
||||
using var server = new ServerTestHarness();
|
||||
using var client01 = new ClientTestHarness();
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
server.HotSwapServerCert();
|
||||
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ServerCertChangeWithoutClientCertUpdateFailsReconnect()
|
||||
{
|
||||
using var server = new ServerTestHarness();
|
||||
using var client01 = new ClientTestHarness();
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
server.HotSwapServerCert();
|
||||
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.WaitForConnectToFail(DefaultTimeout);
|
||||
}
|
||||
|
||||
static X509Certificate2 CreateSelfSignedCertificate(string oid)
|
||||
{
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||
sanBuilder.AddDnsName("localhost");
|
||||
|
||||
using var rsa = RSA.Create();
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
using var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10));
|
||||
var pfxCertificate = new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx),
|
||||
(string)null,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||
|
||||
return pfxCertificate;
|
||||
}
|
||||
|
||||
sealed class ClientTestHarness : IDisposable
|
||||
{
|
||||
readonly HotSwappableClientCertProvider _hotSwapClient = new HotSwappableClientCertProvider();
|
||||
|
||||
IMqttClient _client;
|
||||
|
||||
public string ClientId => _client.Options.ClientId;
|
||||
|
||||
public Task Connect()
|
||||
{
|
||||
using (var server = new ServerTestHarness())
|
||||
using (var client01 = new ClientTestHarness())
|
||||
{
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.HotSwapClientCert();
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.WaitForConnectToFail(DefaultTimeout);
|
||||
}
|
||||
return Run_Client_Connection();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ClientCertChangeWithServerUpdateAcceptsReconnect()
|
||||
public void Dispose()
|
||||
{
|
||||
using (var server = new ServerTestHarness())
|
||||
using (var client01 = new ClientTestHarness())
|
||||
{
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.HotSwapClientCert();
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
}
|
||||
_client.Dispose();
|
||||
_hotSwapClient.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ServerCertChangeWithClientCertUpdateAllowsReconnect()
|
||||
public X509Certificate2 GetCurrentClientCert()
|
||||
{
|
||||
using (var server = new ServerTestHarness())
|
||||
using (var client01 = new ClientTestHarness())
|
||||
{
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
server.HotSwapServerCert();
|
||||
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
}
|
||||
var result = _hotSwapClient.GetCertificates()[0];
|
||||
return new X509Certificate2(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ServerCertChangeWithoutClientCertUpdateFailsReconnect()
|
||||
public void HotSwapClientCert()
|
||||
{
|
||||
using (var server = new ServerTestHarness())
|
||||
using (var client01 = new ClientTestHarness())
|
||||
{
|
||||
server.InstallNewClientCert(client01.GetCurrentClientCert());
|
||||
client01.InstallNewServerCert(server.GetCurrentServerCert());
|
||||
|
||||
await server.StartServer();
|
||||
await client01.Connect();
|
||||
|
||||
client01.WaitForConnectOrFail(DefaultTimeout);
|
||||
server.HotSwapServerCert();
|
||||
|
||||
server.ForceDisconnectAsync(client01).Wait(DefaultTimeout);
|
||||
client01.WaitForDisconnectOrFail(DefaultTimeout);
|
||||
|
||||
client01.WaitForConnectToFail(DefaultTimeout);
|
||||
}
|
||||
_hotSwapClient.HotSwapCert();
|
||||
}
|
||||
|
||||
static X509Certificate2 CreateSelfSignedCertificate(string oid)
|
||||
public void InstallNewServerCert(X509Certificate2 serverCert)
|
||||
{
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||
sanBuilder.AddDnsName("localhost");
|
||||
|
||||
using (var rsa = RSA.Create())
|
||||
{
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
||||
{
|
||||
var pfxCertificate = new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx),
|
||||
(string)null,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||
|
||||
return pfxCertificate;
|
||||
}
|
||||
}
|
||||
_hotSwapClient.InstallNewServerCert(serverCert);
|
||||
}
|
||||
|
||||
sealed class ClientTestHarness : IDisposable
|
||||
public void WaitForConnectOrFail(TimeSpan timeout)
|
||||
{
|
||||
readonly HotSwappableClientCertProvider _hotSwapClient = new HotSwappableClientCertProvider();
|
||||
Thread.Sleep(100);
|
||||
|
||||
IMqttClient _client;
|
||||
|
||||
public string ClientId => _client.Options.ClientId;
|
||||
|
||||
public Task Connect()
|
||||
if (!_client.IsConnected)
|
||||
{
|
||||
return Run_Client_Connection();
|
||||
_client.ReconnectAsync().Wait(timeout);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
_hotSwapClient.Dispose();
|
||||
}
|
||||
WaitForConnect(timeout);
|
||||
|
||||
public X509Certificate2 GetCurrentClientCert()
|
||||
{
|
||||
var result = _hotSwapClient.GetCertificates()[0];
|
||||
return new X509Certificate2(result);
|
||||
}
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsTrue(_client.IsConnected, $"Client connection failed after {timeout}");
|
||||
}
|
||||
|
||||
public void HotSwapClientCert()
|
||||
{
|
||||
_hotSwapClient.HotSwapCert();
|
||||
}
|
||||
public void WaitForConnectToFail(TimeSpan timeout)
|
||||
{
|
||||
Assert.IsFalse(_client.IsConnected, "Client should be disconnected before waiting for connect.");
|
||||
|
||||
public void InstallNewServerCert(X509Certificate2 serverCert)
|
||||
{
|
||||
_hotSwapClient.InstallNewServerCert(serverCert);
|
||||
}
|
||||
WaitForConnect(timeout);
|
||||
|
||||
public void WaitForConnectOrFail(TimeSpan timeout)
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsFalse(_client.IsConnected, "Client connection success but test wanted fail");
|
||||
}
|
||||
|
||||
void WaitForDisconnect(TimeSpan timeout)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
while ((_client == null || _client.IsConnected) && timer.Elapsed < timeout)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
|
||||
if (!_client.IsConnected)
|
||||
{
|
||||
_client.ReconnectAsync().Wait(timeout);
|
||||
}
|
||||
|
||||
WaitForConnect(timeout);
|
||||
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsTrue(_client.IsConnected, $"Client connection failed after {timeout}");
|
||||
}
|
||||
|
||||
public void WaitForConnectToFail(TimeSpan timeout)
|
||||
{
|
||||
Assert.IsFalse(_client.IsConnected, "Client should be disconnected before waiting for connect.");
|
||||
|
||||
WaitForConnect(timeout);
|
||||
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsFalse(_client.IsConnected, "Client connection success but test wanted fail");
|
||||
}
|
||||
|
||||
void WaitForDisconnect(TimeSpan timeout)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
while ((_client == null || _client.IsConnected) && timer.Elapsed < timeout)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
public void WaitForDisconnectOrFail(TimeSpan timeout)
|
||||
{
|
||||
WaitForDisconnect(timeout);
|
||||
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsFalse(_client.IsConnected, $"Client connection should have disconnected after {timeout}");
|
||||
}
|
||||
|
||||
async Task Run_Client_Connection()
|
||||
{
|
||||
var optionsBuilder = new MqttClientOptionsBuilder()
|
||||
.WithTlsOptions(
|
||||
o => o.WithClientCertificatesProvider(_hotSwapClient)
|
||||
.WithCertificateValidationHandler(_hotSwapClient.OnCertificateValidation)
|
||||
.WithSslProtocols(SslProtocols.Tls12))
|
||||
.WithTcpServer("localhost")
|
||||
.WithCleanSession()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500);
|
||||
|
||||
var mqttClientOptions = optionsBuilder.Build();
|
||||
|
||||
var factory = new MqttClientFactory();
|
||||
var mqttClient = factory.CreateMqttClient();
|
||||
_client = mqttClient;
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions);
|
||||
}
|
||||
|
||||
void WaitForConnect(TimeSpan timeout)
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
while ((_client == null || !_client.IsConnected) && timer.Elapsed < timeout)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ServerTestHarness : IDisposable
|
||||
public void WaitForDisconnectOrFail(TimeSpan timeout)
|
||||
{
|
||||
readonly HotSwappableServerCertProvider _hotSwapServer = new HotSwappableServerCertProvider();
|
||||
WaitForDisconnect(timeout);
|
||||
|
||||
MqttServer _server;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_server != null)
|
||||
{
|
||||
_server.StopAsync().Wait();
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
_hotSwapServer?.Dispose();
|
||||
}
|
||||
|
||||
public Task ForceDisconnectAsync(ClientTestHarness client)
|
||||
{
|
||||
return _server.DisconnectClientAsync(client.ClientId, MqttDisconnectReasonCode.UnspecifiedError);
|
||||
}
|
||||
|
||||
public X509Certificate2 GetCurrentServerCert()
|
||||
{
|
||||
return _hotSwapServer.GetCertificate();
|
||||
}
|
||||
|
||||
public void HotSwapServerCert()
|
||||
{
|
||||
_hotSwapServer.HotSwapCert();
|
||||
}
|
||||
|
||||
public void InstallNewClientCert(X509Certificate2 serverCert)
|
||||
{
|
||||
_hotSwapServer.InstallNewClientCert(serverCert);
|
||||
}
|
||||
|
||||
public Task StartServer()
|
||||
{
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(_hotSwapServer)
|
||||
.WithRemoteCertificateValidationCallback(_hotSwapServer.RemoteCertificateValidationCallback)
|
||||
.WithEncryptedEndpoint()
|
||||
.Build();
|
||||
|
||||
mqttServerOptions.TlsEndpointOptions.ClientCertificateRequired = true;
|
||||
|
||||
_server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
|
||||
return _server.StartAsync();
|
||||
}
|
||||
Assert.IsNotNull(_client, "Client was never initialized");
|
||||
Assert.IsFalse(_client.IsConnected, $"Client connection should have disconnected after {timeout}");
|
||||
}
|
||||
|
||||
class HotSwappableClientCertProvider : IMqttClientCertificatesProvider, IDisposable
|
||||
async Task Run_Client_Connection()
|
||||
{
|
||||
X509Certificate2Collection _certificates;
|
||||
ConcurrentBag<X509Certificate2> _serverCerts = new ConcurrentBag<X509Certificate2>();
|
||||
var optionsBuilder = new MqttClientOptionsBuilder()
|
||||
.WithTlsOptions(
|
||||
o => o.WithClientCertificatesProvider(_hotSwapClient)
|
||||
.WithCertificateValidationHandler(_hotSwapClient.OnCertificateValidation)
|
||||
.WithSslProtocols(SslProtocols.Tls12))
|
||||
.WithTcpServer("localhost")
|
||||
.WithCleanSession()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500);
|
||||
|
||||
public HotSwappableClientCertProvider()
|
||||
{
|
||||
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
|
||||
}
|
||||
var mqttClientOptions = optionsBuilder.Build();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_certificates != null)
|
||||
{
|
||||
foreach (var certs in _certificates)
|
||||
{
|
||||
certs.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
var factory = new MqttClientFactory();
|
||||
var mqttClient = factory.CreateMqttClient();
|
||||
_client = mqttClient;
|
||||
|
||||
public X509CertificateCollection GetCertificates()
|
||||
{
|
||||
return new X509Certificate2Collection(_certificates);
|
||||
}
|
||||
|
||||
public void HotSwapCert()
|
||||
{
|
||||
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
|
||||
}
|
||||
|
||||
public void InstallNewServerCert(X509Certificate2 serverCert)
|
||||
{
|
||||
_serverCerts.Add(serverCert);
|
||||
}
|
||||
|
||||
public bool OnCertificateValidation(MqttClientCertificateValidationEventArgs certContext)
|
||||
{
|
||||
var serverCerts = _serverCerts.ToArray();
|
||||
|
||||
var providedCert = certContext.Certificate.GetRawCertData();
|
||||
for (int i = 0, n = serverCerts.Length; i < n; i++)
|
||||
{
|
||||
var currentcert = serverCerts[i];
|
||||
|
||||
if (currentcert.RawData.SequenceEqual(providedCert))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
await mqttClient.ConnectAsync(mqttClientOptions);
|
||||
}
|
||||
|
||||
sealed class HotSwappableServerCertProvider : ICertificateProvider, IDisposable
|
||||
void WaitForConnect(TimeSpan timeout)
|
||||
{
|
||||
readonly ConcurrentBag<X509Certificate2> _clientCerts = new ConcurrentBag<X509Certificate2>();
|
||||
X509Certificate2 _certificate;
|
||||
|
||||
public HotSwappableServerCertProvider()
|
||||
var timer = Stopwatch.StartNew();
|
||||
while ((_client == null || !_client.IsConnected) && timer.Elapsed < timeout)
|
||||
{
|
||||
_certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_certificate.Dispose();
|
||||
}
|
||||
|
||||
public X509Certificate2 GetCertificate()
|
||||
{
|
||||
return _certificate;
|
||||
}
|
||||
|
||||
public void HotSwapCert()
|
||||
{
|
||||
var newCert = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||
var oldCert = Interlocked.Exchange(ref _certificate, newCert);
|
||||
|
||||
oldCert.Dispose();
|
||||
}
|
||||
|
||||
public void InstallNewClientCert(X509Certificate2 certificate)
|
||||
{
|
||||
_clientCerts.Add(certificate);
|
||||
}
|
||||
|
||||
public bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
var serverCerts = _clientCerts.ToArray();
|
||||
|
||||
var providedCert = certificate.GetRawCertData();
|
||||
for (int i = 0, n = serverCerts.Length; i < n; i++)
|
||||
{
|
||||
var currentCert = serverCerts[i];
|
||||
|
||||
if (currentCert.RawData.SequenceEqual(providedCert))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ServerTestHarness : IDisposable
|
||||
{
|
||||
readonly HotSwappableServerCertProvider _hotSwapServer = new HotSwappableServerCertProvider();
|
||||
|
||||
MqttServer _server;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_server != null)
|
||||
{
|
||||
_server.StopAsync().Wait();
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
_hotSwapServer?.Dispose();
|
||||
}
|
||||
|
||||
public Task ForceDisconnectAsync(ClientTestHarness client)
|
||||
{
|
||||
return _server.DisconnectClientAsync(client.ClientId, MqttDisconnectReasonCode.UnspecifiedError);
|
||||
}
|
||||
|
||||
public X509Certificate2 GetCurrentServerCert()
|
||||
{
|
||||
return _hotSwapServer.GetCertificate();
|
||||
}
|
||||
|
||||
public void HotSwapServerCert()
|
||||
{
|
||||
_hotSwapServer.HotSwapCert();
|
||||
}
|
||||
|
||||
public void InstallNewClientCert(X509Certificate2 serverCert)
|
||||
{
|
||||
_hotSwapServer.InstallNewClientCert(serverCert);
|
||||
}
|
||||
|
||||
public Task StartServer()
|
||||
{
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(_hotSwapServer)
|
||||
.WithRemoteCertificateValidationCallback(_hotSwapServer.RemoteCertificateValidationCallback)
|
||||
.WithEncryptedEndpoint()
|
||||
.Build();
|
||||
|
||||
mqttServerOptions.TlsEndpointOptions.ClientCertificateRequired = true;
|
||||
|
||||
_server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
|
||||
return _server.StartAsync();
|
||||
}
|
||||
}
|
||||
|
||||
class HotSwappableClientCertProvider : IMqttClientCertificatesProvider, IDisposable
|
||||
{
|
||||
X509Certificate2Collection _certificates;
|
||||
ConcurrentBag<X509Certificate2> _serverCerts = new ConcurrentBag<X509Certificate2>();
|
||||
|
||||
public HotSwappableClientCertProvider()
|
||||
{
|
||||
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_certificates != null)
|
||||
{
|
||||
foreach (var certs in _certificates)
|
||||
{
|
||||
certs.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public X509CertificateCollection GetCertificates()
|
||||
{
|
||||
return new X509Certificate2Collection(_certificates);
|
||||
}
|
||||
|
||||
public void HotSwapCert()
|
||||
{
|
||||
_certificates = new X509Certificate2Collection(CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.2"));
|
||||
}
|
||||
|
||||
public void InstallNewServerCert(X509Certificate2 serverCert)
|
||||
{
|
||||
_serverCerts.Add(serverCert);
|
||||
}
|
||||
|
||||
public bool OnCertificateValidation(MqttClientCertificateValidationEventArgs certContext)
|
||||
{
|
||||
var serverCerts = _serverCerts.ToArray();
|
||||
|
||||
var providedCert = certContext.Certificate.GetRawCertData();
|
||||
for (int i = 0, n = serverCerts.Length; i < n; i++)
|
||||
{
|
||||
var currentcert = serverCerts[i];
|
||||
|
||||
if (currentcert.RawData.SequenceEqual(providedCert))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class HotSwappableServerCertProvider : ICertificateProvider, IDisposable
|
||||
{
|
||||
readonly ConcurrentBag<X509Certificate2> _clientCerts = new ConcurrentBag<X509Certificate2>();
|
||||
X509Certificate2 _certificate;
|
||||
|
||||
public HotSwappableServerCertProvider()
|
||||
{
|
||||
_certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_certificate.Dispose();
|
||||
}
|
||||
|
||||
public X509Certificate2 GetCertificate()
|
||||
{
|
||||
return _certificate;
|
||||
}
|
||||
|
||||
public void HotSwapCert()
|
||||
{
|
||||
var newCert = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||
var oldCert = Interlocked.Exchange(ref _certificate, newCert);
|
||||
|
||||
oldCert.Dispose();
|
||||
}
|
||||
|
||||
public void InstallNewClientCert(X509Certificate2 certificate)
|
||||
{
|
||||
_clientCerts.Add(certificate);
|
||||
}
|
||||
|
||||
public bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
var serverCerts = _clientCerts.ToArray();
|
||||
|
||||
var providedCert = certificate.GetRawCertData();
|
||||
for (int i = 0, n = serverCerts.Length; i < n; i++)
|
||||
{
|
||||
var currentCert = serverCerts[i];
|
||||
|
||||
if (currentCert.RawData.SequenceEqual(providedCert))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,449 +7,449 @@ using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Server.Exceptions;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Injection_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Injection_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Application_Message_At_Session_Level()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Application_Message_At_Session_Level()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
var receiver1 = await testEnvironment.ConnectClient();
|
||||
var receiver2 = await testEnvironment.ConnectClient();
|
||||
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
|
||||
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
|
||||
await receiver1.SubscribeAsync("#");
|
||||
await receiver2.SubscribeAsync("#");
|
||||
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.WithTopic("InjectedOne").Build();
|
||||
|
||||
var enqueued = clientStatus.TryEnqueueApplicationMessage(message, out var injectResult);
|
||||
|
||||
Assert.IsTrue(enqueued);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual(injectResult.PacketIdentifier, messageReceivedHandler1.ReceivedEventArgs[0].PacketIdentifier);
|
||||
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
|
||||
// The second receiver should NOT receive the message.
|
||||
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Application_Message_At_Session_Level_QueueOverflow_DropNewMessageStrategy()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
|
||||
|
||||
var server = await testEnvironment.StartServer(
|
||||
builder => builder
|
||||
.WithMaxPendingMessagesPerClient(1)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage));
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
|
||||
server.InterceptingOutboundPacketAsync += async args =>
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
// - The first message is dequeued normally and calls this delay
|
||||
// - The second message fills the outbound queue
|
||||
// - The third message overflows the outbound queue
|
||||
if (args.Packet is MqttPublishPacket)
|
||||
{
|
||||
firstMessageOutboundPacketInterceptedTcs.SetResult();
|
||||
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
|
||||
}
|
||||
};
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
var receiver1 = await testEnvironment.ConnectClient();
|
||||
var receiver2 = await testEnvironment.ConnectClient();
|
||||
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
|
||||
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
|
||||
var firstMessageEvicted = false;
|
||||
var secondMessageEvicted = false;
|
||||
var thirdMessageEvicted = false;
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
|
||||
await receiver1.SubscribeAsync("#");
|
||||
await receiver2.SubscribeAsync("#");
|
||||
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.WithTopic("InjectedOne").Build();
|
||||
|
||||
var enqueued = clientStatus.TryEnqueueApplicationMessage(message, out var injectResult);
|
||||
|
||||
Assert.IsTrue(enqueued);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual(injectResult.PacketIdentifier, messageReceivedHandler1.ReceivedEventArgs[0].PacketIdentifier);
|
||||
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
|
||||
// The second receiver should NOT receive the message.
|
||||
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Application_Message_At_Session_Level_QueueOverflow_DropNewMessageStrategy()
|
||||
server.QueuedApplicationMessageOverwrittenAsync += args =>
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
|
||||
|
||||
var server = await testEnvironment.StartServer(
|
||||
builder => builder
|
||||
.WithMaxPendingMessagesPerClient(1)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage));
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
|
||||
server.InterceptingOutboundPacketAsync += async args =>
|
||||
if (args.Packet is not MqttPublishPacket publishPacket)
|
||||
{
|
||||
// - The first message is dequeued normally and calls this delay
|
||||
// - The second message fills the outbound queue
|
||||
// - The third message overflows the outbound queue
|
||||
if (args.Packet is MqttPublishPacket)
|
||||
{
|
||||
firstMessageOutboundPacketInterceptedTcs.SetResult();
|
||||
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
|
||||
}
|
||||
};
|
||||
|
||||
var firstMessageEvicted = false;
|
||||
var secondMessageEvicted = false;
|
||||
var thirdMessageEvicted = false;
|
||||
|
||||
server.QueuedApplicationMessageOverwrittenAsync += args =>
|
||||
{
|
||||
if (args.Packet is not MqttPublishPacket publishPacket)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
switch (publishPacket.Topic)
|
||||
{
|
||||
case "InjectedOne":
|
||||
firstMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedTwo":
|
||||
secondMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedThree":
|
||||
thirdMessageEvicted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
await receiver.SubscribeAsync("#");
|
||||
switch (publishPacket.Topic)
|
||||
{
|
||||
case "InjectedOne":
|
||||
firstMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedTwo":
|
||||
secondMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedThree":
|
||||
thirdMessageEvicted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var firstMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build(), out _);
|
||||
await firstMessageOutboundPacketInterceptedTcs.Task;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
var secondMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build(), out _);
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var thirdMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build(), out _);
|
||||
var firstMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build(), out _);
|
||||
await firstMessageOutboundPacketInterceptedTcs.Task;
|
||||
|
||||
// Due to the DropNewMessage strategy the third message will not be enqueued.
|
||||
// As a result, no existing messages in the queue will be dropped (evicted).
|
||||
Assert.IsTrue(firstMessageEnqueued);
|
||||
Assert.IsTrue(secondMessageEnqueued);
|
||||
Assert.IsFalse(thirdMessageEnqueued);
|
||||
var secondMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build(), out _);
|
||||
|
||||
Assert.IsFalse(firstMessageEvicted);
|
||||
Assert.IsFalse(secondMessageEvicted);
|
||||
Assert.IsFalse(thirdMessageEvicted);
|
||||
}
|
||||
var thirdMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build(), out _);
|
||||
|
||||
// Due to the DropNewMessage strategy the third message will not be enqueued.
|
||||
// As a result, no existing messages in the queue will be dropped (evicted).
|
||||
Assert.IsTrue(firstMessageEnqueued);
|
||||
Assert.IsTrue(secondMessageEnqueued);
|
||||
Assert.IsFalse(thirdMessageEnqueued);
|
||||
|
||||
Assert.IsFalse(firstMessageEvicted);
|
||||
Assert.IsFalse(secondMessageEvicted);
|
||||
Assert.IsFalse(thirdMessageEvicted);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Application_Message_At_Session_Level_QueueOverflow_DropOldestQueuedMessageStrategy()
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Application_Message_At_Session_Level_QueueOverflow_DropOldestQueuedMessageStrategy()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
|
||||
|
||||
var server = await testEnvironment.StartServer(
|
||||
builder => builder
|
||||
.WithMaxPendingMessagesPerClient(1)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage));
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
|
||||
server.InterceptingOutboundPacketAsync += async args =>
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
|
||||
|
||||
var server = await testEnvironment.StartServer(
|
||||
builder => builder
|
||||
.WithMaxPendingMessagesPerClient(1)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage));
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
|
||||
server.InterceptingOutboundPacketAsync += async args =>
|
||||
// - The first message is dequeued normally and calls this delay
|
||||
// - The second message fills the outbound queue
|
||||
// - The third message overflows the outbound queue
|
||||
if (args.Packet is MqttPublishPacket)
|
||||
{
|
||||
// - The first message is dequeued normally and calls this delay
|
||||
// - The second message fills the outbound queue
|
||||
// - The third message overflows the outbound queue
|
||||
if (args.Packet is MqttPublishPacket)
|
||||
{
|
||||
firstMessageOutboundPacketInterceptedTcs.SetResult();
|
||||
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
|
||||
}
|
||||
};
|
||||
firstMessageOutboundPacketInterceptedTcs.SetResult();
|
||||
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
|
||||
}
|
||||
};
|
||||
|
||||
var firstMessageEvicted = false;
|
||||
var secondMessageEvicted = false;
|
||||
var thirdMessageEvicted = false;
|
||||
var firstMessageEvicted = false;
|
||||
var secondMessageEvicted = false;
|
||||
var thirdMessageEvicted = false;
|
||||
|
||||
server.QueuedApplicationMessageOverwrittenAsync += args =>
|
||||
server.QueuedApplicationMessageOverwrittenAsync += args =>
|
||||
{
|
||||
if (args.Packet is not MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (args.Packet is not MqttPublishPacket publishPacket)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
switch (publishPacket.Topic)
|
||||
{
|
||||
case "InjectedOne":
|
||||
firstMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedTwo":
|
||||
secondMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedThree":
|
||||
thirdMessageEvicted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var firstMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build(), out _);
|
||||
await firstMessageOutboundPacketInterceptedTcs.Task;
|
||||
|
||||
var secondMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build(), out _);
|
||||
|
||||
var thirdMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build(), out _);
|
||||
|
||||
// Due to the DropOldestQueuedMessage strategy, all messages will be enqueued initially.
|
||||
// But the second message will eventually be dropped (evicted) to make room for the third one.
|
||||
Assert.IsTrue(firstMessageEnqueued);
|
||||
Assert.IsTrue(secondMessageEnqueued);
|
||||
Assert.IsTrue(thirdMessageEnqueued);
|
||||
|
||||
Assert.IsFalse(firstMessageEvicted);
|
||||
Assert.IsTrue(secondMessageEvicted);
|
||||
Assert.IsFalse(thirdMessageEvicted);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deliver_Application_Message_At_Session_Level()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
var receiver1 = await testEnvironment.ConnectClient();
|
||||
var receiver2 = await testEnvironment.ConnectClient();
|
||||
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
|
||||
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
|
||||
await receiver1.SubscribeAsync("#");
|
||||
await receiver2.SubscribeAsync("#");
|
||||
|
||||
var mqttApplicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("InjectedOne")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build();
|
||||
var injectResult = await clientStatus.DeliverApplicationMessageAsync(mqttApplicationMessage);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual(injectResult.PacketIdentifier, messageReceivedHandler1.ReceivedEventArgs[0].PacketIdentifier);
|
||||
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
|
||||
// The second receiver should NOT receive the message.
|
||||
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deliver_Application_Message_At_Session_Level_QueueOverflow_DropNewMessageStrategy()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer(
|
||||
builder => builder
|
||||
.WithMaxPendingMessagesPerClient(1)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage));
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
|
||||
server.InterceptingOutboundPacketAsync += async args =>
|
||||
switch (publishPacket.Topic)
|
||||
{
|
||||
// - The first message is dequeued normally and calls this delay
|
||||
// - The second message fills the outbound queue
|
||||
// - The third message overflows the outbound queue
|
||||
if (args.Packet is MqttPublishPacket)
|
||||
{
|
||||
firstMessageOutboundPacketInterceptedTcs.SetResult();
|
||||
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
|
||||
}
|
||||
};
|
||||
case "InjectedOne":
|
||||
firstMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedTwo":
|
||||
secondMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedThree":
|
||||
thirdMessageEvicted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var firstMessageEvicted = false;
|
||||
var secondMessageEvicted = false;
|
||||
var thirdMessageEvicted = false;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
server.QueuedApplicationMessageOverwrittenAsync += args =>
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var firstMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build(), out _);
|
||||
await firstMessageOutboundPacketInterceptedTcs.Task;
|
||||
|
||||
var secondMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build(), out _);
|
||||
|
||||
var thirdMessageEnqueued = clientStatus.TryEnqueueApplicationMessage(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build(), out _);
|
||||
|
||||
// Due to the DropOldestQueuedMessage strategy, all messages will be enqueued initially.
|
||||
// But the second message will eventually be dropped (evicted) to make room for the third one.
|
||||
Assert.IsTrue(firstMessageEnqueued);
|
||||
Assert.IsTrue(secondMessageEnqueued);
|
||||
Assert.IsTrue(thirdMessageEnqueued);
|
||||
|
||||
Assert.IsFalse(firstMessageEvicted);
|
||||
Assert.IsTrue(secondMessageEvicted);
|
||||
Assert.IsFalse(thirdMessageEvicted);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deliver_Application_Message_At_Session_Level()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
var receiver1 = await testEnvironment.ConnectClient();
|
||||
var receiver2 = await testEnvironment.ConnectClient();
|
||||
var messageReceivedHandler1 = testEnvironment.CreateApplicationMessageHandler(receiver1);
|
||||
var messageReceivedHandler2 = testEnvironment.CreateApplicationMessageHandler(receiver2);
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
|
||||
await receiver1.SubscribeAsync("#");
|
||||
await receiver2.SubscribeAsync("#");
|
||||
|
||||
var mqttApplicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("InjectedOne")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build();
|
||||
var injectResult = await clientStatus.DeliverApplicationMessageAsync(mqttApplicationMessage);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, messageReceivedHandler1.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual(injectResult.PacketIdentifier, messageReceivedHandler1.ReceivedEventArgs[0].PacketIdentifier);
|
||||
Assert.AreEqual("InjectedOne", messageReceivedHandler1.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
|
||||
// The second receiver should NOT receive the message.
|
||||
Assert.AreEqual(0, messageReceivedHandler2.ReceivedEventArgs.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deliver_Application_Message_At_Session_Level_QueueOverflow_DropNewMessageStrategy()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer(
|
||||
builder => builder
|
||||
.WithMaxPendingMessagesPerClient(1)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage));
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
|
||||
server.InterceptingOutboundPacketAsync += async args =>
|
||||
{
|
||||
// - The first message is dequeued normally and calls this delay
|
||||
// - The second message fills the outbound queue
|
||||
// - The third message overflows the outbound queue
|
||||
if (args.Packet is MqttPublishPacket)
|
||||
{
|
||||
if (args.Packet is not MqttPublishPacket publishPacket)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
firstMessageOutboundPacketInterceptedTcs.SetResult();
|
||||
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
|
||||
}
|
||||
};
|
||||
|
||||
switch (publishPacket.Topic)
|
||||
{
|
||||
case "InjectedOne":
|
||||
firstMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedTwo":
|
||||
secondMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedThree":
|
||||
thirdMessageEvicted = true;
|
||||
break;
|
||||
}
|
||||
var firstMessageEvicted = false;
|
||||
var secondMessageEvicted = false;
|
||||
var thirdMessageEvicted = false;
|
||||
|
||||
server.QueuedApplicationMessageOverwrittenAsync += args =>
|
||||
{
|
||||
if (args.Packet is not MqttPublishPacket publishPacket)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
await receiver.SubscribeAsync("#");
|
||||
switch (publishPacket.Topic)
|
||||
{
|
||||
case "InjectedOne":
|
||||
firstMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedTwo":
|
||||
secondMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedThree":
|
||||
thirdMessageEvicted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var firstMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build()));
|
||||
await LongTestDelay();
|
||||
await firstMessageOutboundPacketInterceptedTcs.Task;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
var secondMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build()));
|
||||
await LongTestDelay();
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var thirdMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build()));
|
||||
await LongTestDelay();
|
||||
var firstMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build()));
|
||||
await LongTestDelay();
|
||||
await firstMessageOutboundPacketInterceptedTcs.Task;
|
||||
|
||||
Task.WaitAny(firstMessageTask, secondMessageTask, thirdMessageTask);
|
||||
var secondMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build()));
|
||||
await LongTestDelay();
|
||||
|
||||
// Due to the DropNewMessage strategy the third message delivery will fail.
|
||||
// As a result, no existing messages in the queue will be dropped (evicted).
|
||||
Assert.AreEqual(firstMessageTask.Status, TaskStatus.WaitingForActivation);
|
||||
Assert.AreEqual(secondMessageTask.Status, TaskStatus.WaitingForActivation);
|
||||
Assert.AreEqual(thirdMessageTask.Status, TaskStatus.Faulted);
|
||||
Assert.IsTrue(thirdMessageTask.Exception?.InnerException is MqttPendingMessagesOverflowException);
|
||||
var thirdMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build()));
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsFalse(firstMessageEvicted);
|
||||
Assert.IsFalse(secondMessageEvicted);
|
||||
Assert.IsFalse(thirdMessageEvicted);
|
||||
}
|
||||
Task.WaitAny(firstMessageTask, secondMessageTask, thirdMessageTask);
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deliver_Application_Message_At_Session_Level_QueueOverflow_DropOldestQueuedMessageStrategy()
|
||||
// Due to the DropNewMessage strategy the third message delivery will fail.
|
||||
// As a result, no existing messages in the queue will be dropped (evicted).
|
||||
Assert.AreEqual(firstMessageTask.Status, TaskStatus.WaitingForActivation);
|
||||
Assert.AreEqual(secondMessageTask.Status, TaskStatus.WaitingForActivation);
|
||||
Assert.AreEqual(thirdMessageTask.Status, TaskStatus.Faulted);
|
||||
Assert.IsTrue(thirdMessageTask.Exception?.InnerException is MqttPendingMessagesOverflowException);
|
||||
|
||||
Assert.IsFalse(firstMessageEvicted);
|
||||
Assert.IsFalse(secondMessageEvicted);
|
||||
Assert.IsFalse(thirdMessageEvicted);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deliver_Application_Message_At_Session_Level_QueueOverflow_DropOldestQueuedMessageStrategy()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
|
||||
|
||||
var server = await testEnvironment.StartServer(
|
||||
builder => builder
|
||||
.WithMaxPendingMessagesPerClient(1)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage));
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
|
||||
server.InterceptingOutboundPacketAsync += async args =>
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(trackUnobservedTaskException: false);
|
||||
|
||||
var server = await testEnvironment.StartServer(
|
||||
builder => builder
|
||||
.WithMaxPendingMessagesPerClient(1)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage));
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var firstMessageOutboundPacketInterceptedTcs = new TaskCompletionSource();
|
||||
server.InterceptingOutboundPacketAsync += async args =>
|
||||
// - The first message is dequeued normally and calls this delay
|
||||
// - The second message fills the outbound queue
|
||||
// - The third message overflows the outbound queue
|
||||
if (args.Packet is MqttPublishPacket)
|
||||
{
|
||||
// - The first message is dequeued normally and calls this delay
|
||||
// - The second message fills the outbound queue
|
||||
// - The third message overflows the outbound queue
|
||||
if (args.Packet is MqttPublishPacket)
|
||||
{
|
||||
firstMessageOutboundPacketInterceptedTcs.SetResult();
|
||||
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
|
||||
}
|
||||
};
|
||||
firstMessageOutboundPacketInterceptedTcs.SetResult();
|
||||
await Task.Delay(TimeSpan.FromDays(1), args.CancellationToken);
|
||||
}
|
||||
};
|
||||
|
||||
var firstMessageEvicted = false;
|
||||
var secondMessageEvicted = false;
|
||||
var thirdMessageEvicted = false;
|
||||
var firstMessageEvicted = false;
|
||||
var secondMessageEvicted = false;
|
||||
var thirdMessageEvicted = false;
|
||||
|
||||
server.QueuedApplicationMessageOverwrittenAsync += args =>
|
||||
server.QueuedApplicationMessageOverwrittenAsync += args =>
|
||||
{
|
||||
if (args.Packet is not MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (args.Packet is not MqttPublishPacket publishPacket)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
switch (publishPacket.Topic)
|
||||
{
|
||||
case "InjectedOne":
|
||||
firstMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedTwo":
|
||||
secondMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedThree":
|
||||
thirdMessageEvicted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var firstMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build()));
|
||||
await LongTestDelay();
|
||||
await firstMessageOutboundPacketInterceptedTcs.Task;
|
||||
|
||||
var secondMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build()));
|
||||
await LongTestDelay();
|
||||
|
||||
var thirdMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build()));
|
||||
await LongTestDelay();
|
||||
|
||||
Task.WaitAny(firstMessageTask, secondMessageTask, thirdMessageTask);
|
||||
|
||||
// Due to the DropOldestQueuedMessage strategy, the second message delivery will fail
|
||||
// to make room for the third one.
|
||||
Assert.AreEqual(firstMessageTask.Status, TaskStatus.WaitingForActivation);
|
||||
Assert.AreEqual(secondMessageTask.Status, TaskStatus.Faulted);
|
||||
Assert.IsTrue(secondMessageTask.Exception?.InnerException is MqttPendingMessagesOverflowException);
|
||||
Assert.AreEqual(thirdMessageTask.Status, TaskStatus.WaitingForActivation);
|
||||
|
||||
Assert.IsFalse(firstMessageEvicted);
|
||||
Assert.IsTrue(secondMessageEvicted);
|
||||
Assert.IsFalse(thirdMessageEvicted);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Inject_ApplicationMessage_At_Server_Level()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageReceivedHandler = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
|
||||
|
||||
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, messageReceivedHandler.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("InjectedOne", messageReceivedHandler.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Injected_Application_Message()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
MqttApplicationMessage interceptedMessage = null;
|
||||
server.InterceptingPublishAsync += eventArgs =>
|
||||
switch (publishPacket.Topic)
|
||||
{
|
||||
interceptedMessage = eventArgs.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
case "InjectedOne":
|
||||
firstMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedTwo":
|
||||
secondMessageEvicted = true;
|
||||
break;
|
||||
case "InjectedThree":
|
||||
thirdMessageEvicted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
|
||||
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await LongTestDelay();
|
||||
var status = await server.GetSessionsAsync();
|
||||
var clientStatus = status[0];
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
Assert.IsNotNull(interceptedMessage);
|
||||
}
|
||||
var firstMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build()));
|
||||
await LongTestDelay();
|
||||
await firstMessageOutboundPacketInterceptedTcs.Task;
|
||||
|
||||
var secondMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedTwo").Build()));
|
||||
await LongTestDelay();
|
||||
|
||||
var thirdMessageTask = Task.Run(
|
||||
() => clientStatus.DeliverApplicationMessageAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("InjectedThree").Build()));
|
||||
await LongTestDelay();
|
||||
|
||||
Task.WaitAny(firstMessageTask, secondMessageTask, thirdMessageTask);
|
||||
|
||||
// Due to the DropOldestQueuedMessage strategy, the second message delivery will fail
|
||||
// to make room for the third one.
|
||||
Assert.AreEqual(firstMessageTask.Status, TaskStatus.WaitingForActivation);
|
||||
Assert.AreEqual(secondMessageTask.Status, TaskStatus.Faulted);
|
||||
Assert.IsTrue(secondMessageTask.Exception?.InnerException is MqttPendingMessagesOverflowException);
|
||||
Assert.AreEqual(thirdMessageTask.Status, TaskStatus.WaitingForActivation);
|
||||
|
||||
Assert.IsFalse(firstMessageEvicted);
|
||||
Assert.IsTrue(secondMessageEvicted);
|
||||
Assert.IsFalse(thirdMessageEvicted);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Inject_ApplicationMessage_At_Server_Level()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageReceivedHandler = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
|
||||
await receiver.SubscribeAsync("#");
|
||||
|
||||
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
|
||||
|
||||
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, messageReceivedHandler.ReceivedEventArgs.Count);
|
||||
Assert.AreEqual("InjectedOne", messageReceivedHandler.ReceivedEventArgs[0].ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Injected_Application_Message()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
MqttApplicationMessage interceptedMessage = null;
|
||||
server.InterceptingPublishAsync += eventArgs =>
|
||||
{
|
||||
interceptedMessage = eventArgs.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var injectedApplicationMessage = new MqttApplicationMessageBuilder().WithTopic("InjectedOne").Build();
|
||||
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(injectedApplicationMessage));
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.IsNotNull(interceptedMessage);
|
||||
}
|
||||
}
|
||||
@@ -10,59 +10,57 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class KeepAlive_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class KeepAlive_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Client_DueTo_KeepAlive()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Client_DueTo_KeepAlive()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectLowLevelClient(o => o
|
||||
.WithTimeout(TimeSpan.FromSeconds(1))
|
||||
.WithTimeout(TimeSpan.Zero)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)).ConfigureAwait(false);
|
||||
|
||||
await client.SendAsync(new MqttConnectPacket
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
CleanSession = true,
|
||||
ClientId = "Disconnect_Client_DueTo_KeepAlive",
|
||||
KeepAlivePeriod = 1
|
||||
}, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var client = await testEnvironment.ConnectLowLevelClient(o => o
|
||||
.WithTimeout(TimeSpan.FromSeconds(1))
|
||||
.WithTimeout(TimeSpan.Zero)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)).ConfigureAwait(false);
|
||||
var responsePacket = await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
Assert.IsTrue(responsePacket is MqttConnAckPacket);
|
||||
|
||||
await client.SendAsync(new MqttConnectPacket
|
||||
{
|
||||
CleanSession = true,
|
||||
ClientId = "Disconnect_Client_DueTo_KeepAlive",
|
||||
KeepAlivePeriod = 1
|
||||
}, CancellationToken.None).ConfigureAwait(false);
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
|
||||
var responsePacket = await client.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
Assert.IsTrue(responsePacket is MqttConnAckPacket);
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
Assert.IsTrue(responsePacket is MqttPingRespPacket);
|
||||
}
|
||||
|
||||
// If we reach this point everything works as expected (server did not close the connection
|
||||
// due to proper ping messages.
|
||||
// Now we will wait 1.1 seconds because the server MUST wait 1.5 seconds in total (See spec).
|
||||
|
||||
await Task.Delay(1100);
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
Assert.IsTrue(responsePacket is MqttPingRespPacket);
|
||||
|
||||
// Now we will wait longer than 1.5 so that the server will close the connection.
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
|
||||
var disconnectPacket = responsePacket as MqttDisconnectPacket;
|
||||
|
||||
Assert.IsTrue(disconnectPacket != null);
|
||||
Assert.AreEqual(disconnectPacket.ReasonCode, MqttDisconnectReasonCode.KeepAliveTimeout);
|
||||
}
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
Assert.IsTrue(responsePacket is MqttPingRespPacket);
|
||||
}
|
||||
|
||||
// If we reach this point everything works as expected (server did not close the connection
|
||||
// due to proper ping messages.
|
||||
// Now we will wait 1.1 seconds because the server MUST wait 1.5 seconds in total (See spec).
|
||||
|
||||
await Task.Delay(1100);
|
||||
await client.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
Assert.IsTrue(responsePacket is MqttPingRespPacket);
|
||||
|
||||
// Now we will wait longer than 1.5 so that the server will close the connection.
|
||||
responsePacket = await client.ReceiveAsync(CancellationToken.None);
|
||||
|
||||
var disconnectPacket = responsePacket as MqttDisconnectPacket;
|
||||
|
||||
Assert.IsTrue(disconnectPacket != null);
|
||||
Assert.AreEqual(disconnectPacket.ReasonCode, MqttDisconnectReasonCode.KeepAliveTimeout);
|
||||
}
|
||||
}
|
||||
@@ -6,166 +6,154 @@ using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
#if DEBUG
|
||||
[TestClass]
|
||||
#endif
|
||||
public sealed class Load_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Load_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Low_Level_Client()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Receiving_Client()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
server.InterceptingPublishAsync += _ =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
using (var receiverClient = await testEnvironment.ConnectClient())
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
_ = Task.Factory.StartNew(
|
||||
async () =>
|
||||
{
|
||||
receiverClient.ApplicationMessageReceivedAsync += e =>
|
||||
try
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
using var client = await testEnvironment.ConnectLowLevelClient();
|
||||
|
||||
await receiverClient.SubscribeAsync("t/+");
|
||||
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
await client.SendAsync(
|
||||
new MqttConnectPacket
|
||||
{
|
||||
using (var client = await testEnvironment.ConnectClient())
|
||||
{
|
||||
var applicationMessageBuilder = new MqttApplicationMessageBuilder();
|
||||
ClientId = "Handle_100_000_Messages_In_Low_Level_Client_" + Guid.NewGuid()
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
var message = applicationMessageBuilder.WithTopic("t/" + j)
|
||||
.Build();
|
||||
var packet = await client.ReceiveAsync(CancellationToken.None);
|
||||
|
||||
await client.PublishAsync(message)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
var connAckPacket = packet as MqttConnAckPacket;
|
||||
|
||||
await client.DisconnectAsync();
|
||||
}
|
||||
});
|
||||
Assert.IsTrue(connAckPacket != null);
|
||||
Assert.AreEqual(MqttConnectReasonCode.Success, connAckPacket.ReasonCode);
|
||||
|
||||
var publishPacket = new MqttPublishPacket();
|
||||
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
publishPacket.Topic = j.ToString();
|
||||
|
||||
await client.SendAsync(publishPacket, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
},
|
||||
TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
var result = SpinWait.SpinUntil(() => receivedMessages >= 100000, TimeSpan.FromMinutes(5));
|
||||
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Receiving_Client()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
using var receiverClient = await testEnvironment.ConnectClient();
|
||||
|
||||
receiverClient.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await receiverClient.SubscribeAsync("t/+");
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
_ = Task.Factory.StartNew(
|
||||
async () =>
|
||||
{
|
||||
using var client = await testEnvironment.ConnectClient();
|
||||
var applicationMessageBuilder = testEnvironment.ClientFactory.CreateApplicationMessageBuilder();
|
||||
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
var message = applicationMessageBuilder.WithTopic("t/" + j).Build();
|
||||
|
||||
await client.PublishAsync(message).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(120));
|
||||
|
||||
Assert.AreEqual(100000, receivedMessages);
|
||||
}
|
||||
}
|
||||
await client.DisconnectAsync();
|
||||
},
|
||||
TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Low_Level_Client()
|
||||
var result = SpinWait.SpinUntil(() => receivedMessages >= 100000, TimeSpan.FromMinutes(5));
|
||||
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Server()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
server.InterceptingPublishAsync += _ =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var client = await testEnvironment.ConnectLowLevelClient())
|
||||
{
|
||||
await client.SendAsync(
|
||||
new MqttConnectPacket
|
||||
{
|
||||
ClientId = "Handle_100_000_Messages_In_Low_Level_Client_" + Guid.NewGuid()
|
||||
}, CancellationToken.None);
|
||||
|
||||
var packet = await client.ReceiveAsync(CancellationToken.None);
|
||||
|
||||
var connAckPacket = packet as MqttConnAckPacket;
|
||||
|
||||
Assert.IsTrue(connAckPacket != null);
|
||||
Assert.AreEqual(MqttConnectReasonCode.Success, connAckPacket.ReasonCode);
|
||||
|
||||
var publishPacket = new MqttPublishPacket();
|
||||
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
publishPacket.Topic = j.ToString();
|
||||
|
||||
await client.SendAsync(publishPacket, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(10));
|
||||
|
||||
Assert.AreEqual(100000, receivedMessages);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Handle_100_000_Messages_In_Server()
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = 0;
|
||||
|
||||
server.InterceptingPublishAsync += e =>
|
||||
_ = Task.Factory.StartNew(
|
||||
async () =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessages);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
using var client = await testEnvironment.ConnectClient();
|
||||
var applicationMessageBuilder = new MqttApplicationMessageBuilder();
|
||||
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (var client = await testEnvironment.ConnectClient())
|
||||
{
|
||||
var applicationMessageBuilder = new MqttApplicationMessageBuilder();
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
var message = applicationMessageBuilder.WithTopic(j.ToString()).Build();
|
||||
|
||||
for (var j = 0; j < 1000; j++)
|
||||
{
|
||||
var message = applicationMessageBuilder.WithTopic(j.ToString())
|
||||
.Build();
|
||||
await client.PublishAsync(message).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await client.PublishAsync(message)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await client.DisconnectAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessages == 100000, TimeSpan.FromSeconds(10));
|
||||
|
||||
Assert.AreEqual(100000, receivedMessages);
|
||||
}
|
||||
await client.DisconnectAsync();
|
||||
},
|
||||
TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
var result = SpinWait.SpinUntil(() => receivedMessages >= 100000, TimeSpan.FromMinutes(5));
|
||||
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,22 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Server.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttRetainedMessageManager_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttRetainedMessageManager_Tests
|
||||
[TestMethod]
|
||||
public async Task MqttRetainedMessageManager_GetUndefinedTopic()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task MqttRetainedMessageManager_GetUndefinedTopic()
|
||||
{
|
||||
var logger = new Mockups.TestLogger();
|
||||
var eventContainer = new MqttServerEventContainer();
|
||||
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
||||
var task = retainedMessagesManager.GetMessage("undefined");
|
||||
Assert.IsNotNull(task, "Task should not be null");
|
||||
var result = await task;
|
||||
Assert.IsNull(result, "Null result expected");
|
||||
}
|
||||
var logger = new Mockups.TestLogger();
|
||||
var eventContainer = new MqttServerEventContainer();
|
||||
var retainedMessagesManager = new MqttRetainedMessagesManager(eventContainer, logger);
|
||||
var task = retainedMessagesManager.GetMessage("undefined");
|
||||
Assert.IsNotNull(task, "Task should not be null");
|
||||
var result = await task;
|
||||
Assert.IsNull(result, "Null result expected");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -13,227 +12,218 @@ using MQTTnet.Server;
|
||||
using MQTTnet.Server.Internal;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttSubscriptionsManager_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttSubscriptionsManager_Tests : BaseTestClass
|
||||
MqttClientSubscriptionsManager _subscriptionsManager;
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle()
|
||||
{
|
||||
MqttClientSubscriptionsManager _subscriptionsManager;
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeAndUnsubscribeSingle()
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
|
||||
}
|
||||
};
|
||||
TopicFilters = [new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()]
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
Assert.IsTrue(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
Assert.IsTrue(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
|
||||
var up = new MqttUnsubscribePacket();
|
||||
up.TopicFilters.Add("A/B/C");
|
||||
await _subscriptionsManager.Unsubscribe(up, CancellationToken.None);
|
||||
var up = new MqttUnsubscribePacket();
|
||||
up.TopicFilters.Add("A/B/C");
|
||||
await _subscriptionsManager.Unsubscribe(up, CancellationToken.None);
|
||||
|
||||
Assert.IsFalse(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
}
|
||||
Assert.IsFalse(CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeDifferentQoSSuccess()
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeDifferentQoSSuccess()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }
|
||||
}
|
||||
};
|
||||
TopicFilters =
|
||||
[
|
||||
new() { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }
|
||||
]
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeSingleNoSuccess()
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeSingleNoSuccess()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
|
||||
}
|
||||
};
|
||||
TopicFilters = [new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()]
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
Assert.IsFalse(CheckSubscriptions("A/B/X", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
}
|
||||
Assert.IsFalse(CheckSubscriptions("A/B/X", MqttQualityOfServiceLevel.AtMostOnce, "").IsSubscribed);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeSingleSuccess()
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeSingleSuccess()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()
|
||||
}
|
||||
};
|
||||
TopicFilters = [new MqttTopicFilterBuilder().WithTopic("A/B/C").Build()]
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "");
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "");
|
||||
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeTwoTimesSuccess()
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeTwoTimesSuccess()
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter { Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce },
|
||||
new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }
|
||||
}
|
||||
};
|
||||
TopicFilters =
|
||||
[
|
||||
new MqttTopicFilter { Topic = "#", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce },
|
||||
new MqttTopicFilter { Topic = "A/B/C", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }
|
||||
]
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
|
||||
var result = CheckSubscriptions("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "");
|
||||
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard1()
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard1()
|
||||
{
|
||||
await SubscribeToTopic("house/+/room");
|
||||
await SubscribeToTopic("house/+/room/+");
|
||||
|
||||
CheckIsSubscribed("house/1/room");
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/1/room/chair");
|
||||
CheckIsSubscribed("house/2/room/bed");
|
||||
CheckIsSubscribed("house/2/room/chair");
|
||||
|
||||
CheckIsNotSubscribed("house/1/room/bed/cover");
|
||||
CheckIsNotSubscribed("house/1/study/bed");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard2()
|
||||
{
|
||||
await SubscribeToTopic("house/+/room");
|
||||
await SubscribeToTopic("house/+/room/#");
|
||||
|
||||
CheckIsSubscribed("house/1/room");
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/2/room");
|
||||
CheckIsSubscribed("house/2/room/bed");
|
||||
CheckIsSubscribed("house/2/room/bed/cover");
|
||||
|
||||
CheckIsNotSubscribed("house/1/level");
|
||||
CheckIsNotSubscribed("house/1/level/door");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard3()
|
||||
{
|
||||
await SubscribeToTopic("house/1/room");
|
||||
await SubscribeToTopic("house/1/room/+");
|
||||
|
||||
CheckIsSubscribed("house/1/room");
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/1/room/chair");
|
||||
|
||||
CheckIsNotSubscribed("house/2/room");
|
||||
CheckIsNotSubscribed("house/2/room/bed/cover");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard4()
|
||||
{
|
||||
await SubscribeToTopic("house/1/+/+");
|
||||
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/1/room/chair");
|
||||
|
||||
CheckIsNotSubscribed("house/1/room");
|
||||
CheckIsNotSubscribed("house/1/room/bed/cover");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard5()
|
||||
{
|
||||
await SubscribeToTopic("house/1/+/#");
|
||||
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/1/room/chair");
|
||||
CheckIsSubscribed("house/1/room/chair/leg");
|
||||
CheckIsSubscribed("house/1/level/window");
|
||||
CheckIsSubscribed("house/1/level/door");
|
||||
|
||||
CheckIsNotSubscribed("house/2/room/bed");
|
||||
}
|
||||
|
||||
async Task SubscribeToTopic(string topic)
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
await SubscribeToTopic("house/+/room");
|
||||
await SubscribeToTopic("house/+/room/+");
|
||||
TopicFilters =
|
||||
[
|
||||
new MqttTopicFilter { Topic = topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce }
|
||||
]
|
||||
};
|
||||
|
||||
CheckIsSubscribed("house/1/room");
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/1/room/chair");
|
||||
CheckIsSubscribed("house/2/room/bed");
|
||||
CheckIsSubscribed("house/2/room/chair");
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
CheckIsNotSubscribed("house/1/room/bed/cover");
|
||||
CheckIsNotSubscribed("house/1/study/bed");
|
||||
}
|
||||
void CheckIsSubscribed(string topic)
|
||||
{
|
||||
var result = CheckSubscriptions(topic, MqttQualityOfServiceLevel.AtMostOnce, "");
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard2()
|
||||
void CheckIsNotSubscribed(string topic)
|
||||
{
|
||||
var result = CheckSubscriptions(topic, MqttQualityOfServiceLevel.AtMostOnce, "");
|
||||
Assert.IsFalse(result.IsSubscribed);
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
var logger = new TestLogger();
|
||||
var options = new MqttServerOptions();
|
||||
var retainedMessagesManager = new MqttRetainedMessagesManager(new MqttServerEventContainer(), logger);
|
||||
var eventContainer = new MqttServerEventContainer();
|
||||
var clientSessionManager = new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger);
|
||||
|
||||
var session = new MqttSession(new MqttConnectPacket
|
||||
{
|
||||
await SubscribeToTopic("house/+/room");
|
||||
await SubscribeToTopic("house/+/room/#");
|
||||
ClientId = ""
|
||||
}, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
|
||||
|
||||
CheckIsSubscribed("house/1/room");
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/2/room");
|
||||
CheckIsSubscribed("house/2/room/bed");
|
||||
CheckIsSubscribed("house/2/room/bed/cover");
|
||||
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(), retainedMessagesManager, clientSessionManager);
|
||||
}
|
||||
|
||||
CheckIsNotSubscribed("house/1/level");
|
||||
CheckIsNotSubscribed("house/1/level/door");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard3()
|
||||
{
|
||||
await SubscribeToTopic("house/1/room");
|
||||
await SubscribeToTopic("house/1/room/+");
|
||||
|
||||
CheckIsSubscribed("house/1/room");
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/1/room/chair");
|
||||
|
||||
CheckIsNotSubscribed("house/2/room");
|
||||
CheckIsNotSubscribed("house/2/room/bed/cover");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard4()
|
||||
{
|
||||
await SubscribeToTopic("house/1/+/+");
|
||||
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/1/room/chair");
|
||||
|
||||
CheckIsNotSubscribed("house/1/room");
|
||||
CheckIsNotSubscribed("house/1/room/bed/cover");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task MqttSubscriptionsManager_SubscribeWildcard5()
|
||||
{
|
||||
await SubscribeToTopic("house/1/+/#");
|
||||
|
||||
CheckIsSubscribed("house/1/room/bed");
|
||||
CheckIsSubscribed("house/1/room/chair");
|
||||
CheckIsSubscribed("house/1/room/chair/leg");
|
||||
CheckIsSubscribed("house/1/level/window");
|
||||
CheckIsSubscribed("house/1/level/door");
|
||||
|
||||
CheckIsNotSubscribed("house/2/room/bed");
|
||||
}
|
||||
|
||||
async Task SubscribeToTopic(string topic)
|
||||
{
|
||||
var sp = new MqttSubscribePacket
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter { Topic = topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce },
|
||||
}
|
||||
};
|
||||
|
||||
await _subscriptionsManager.Subscribe(sp, CancellationToken.None);
|
||||
}
|
||||
|
||||
void CheckIsSubscribed(string topic)
|
||||
{
|
||||
var result = CheckSubscriptions(topic, MqttQualityOfServiceLevel.AtMostOnce, "");
|
||||
Assert.IsTrue(result.IsSubscribed);
|
||||
Assert.AreEqual(result.QualityOfServiceLevel, MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
void CheckIsNotSubscribed(string topic)
|
||||
{
|
||||
var result = CheckSubscriptions(topic, MqttQualityOfServiceLevel.AtMostOnce, "");
|
||||
Assert.IsFalse(result.IsSubscribed);
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
var logger = new TestLogger();
|
||||
var options = new MqttServerOptions();
|
||||
var retainedMessagesManager = new MqttRetainedMessagesManager(new MqttServerEventContainer(), logger);
|
||||
var eventContainer = new MqttServerEventContainer();
|
||||
var clientSessionManager = new MqttClientSessionsManager(options, retainedMessagesManager, eventContainer, logger);
|
||||
|
||||
var session = new MqttSession(new MqttConnectPacket
|
||||
{
|
||||
ClientId = ""
|
||||
}, new ConcurrentDictionary<object, object>(), options, eventContainer, retainedMessagesManager, clientSessionManager);
|
||||
|
||||
_subscriptionsManager = new MqttClientSubscriptionsManager(session, new MqttServerEventContainer(), retainedMessagesManager, clientSessionManager);
|
||||
}
|
||||
|
||||
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
||||
{
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
|
||||
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
||||
}
|
||||
CheckSubscriptionsResult CheckSubscriptions(string topic, MqttQualityOfServiceLevel applicationMessageQoSLevel, string senderClientId)
|
||||
{
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
|
||||
return _subscriptionsManager.CheckSubscriptions(topic, topicHash, applicationMessageQoSLevel, senderClientId);
|
||||
}
|
||||
}
|
||||
@@ -6,45 +6,43 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class No_Local_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class No_Local_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public Task Subscribe_With_No_Local()
|
||||
{
|
||||
[TestMethod]
|
||||
public Task Subscribe_With_No_Local()
|
||||
{
|
||||
return ExecuteTest(true, 0);
|
||||
}
|
||||
return ExecuteTest(true, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Subscribe_Without_No_Local()
|
||||
{
|
||||
return ExecuteTest(false, 1);
|
||||
}
|
||||
[TestMethod]
|
||||
public Task Subscribe_Without_No_Local()
|
||||
{
|
||||
return ExecuteTest(false, 1);
|
||||
}
|
||||
|
||||
async Task ExecuteTest(
|
||||
bool noLocal,
|
||||
int expectedCountAfterPublish)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
async Task ExecuteTest(
|
||||
bool noLocal,
|
||||
int expectedCountAfterPublish)
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithNoLocal(noLocal).Build();
|
||||
await client1.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithNoLocal(noLocal).Build();
|
||||
await client1.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterPublish);
|
||||
}
|
||||
}
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterPublish);
|
||||
}
|
||||
}
|
||||
@@ -10,138 +10,128 @@ using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Publishing_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Publishing_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Publishing()
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Publishing()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
// The client will be disconnected directly after subscribing!
|
||||
server.InterceptingPublishAsync += ev => server.DisconnectClientAsync(ev.ClientId);
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_NoMatchingSubscribers_When_Not_Subscribed()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
await receiver.SubscribeAsync("A");
|
||||
|
||||
// AtLeastOnce is required to get an ACK packet from the server.
|
||||
var publishResult = await sender.PublishStringAsync("B", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, publishResult.ReasonCode);
|
||||
|
||||
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_Success_When_Subscribed()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
await receiver.SubscribeAsync("A");
|
||||
|
||||
// AtLeastOnce is required to get an ACK packet from the server.
|
||||
var publishResult = await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, publishResult.ReasonCode);
|
||||
|
||||
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Client_Enqueue()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
await receiver.SubscribeAsync("A");
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(1);
|
||||
|
||||
server.InterceptingClientEnqueueAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
e.AcceptEnqueue = false;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
// The client will be disconnect directly after subscribing!
|
||||
server.InterceptingPublishAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.PublishStringAsync("test", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
await LongTestDelay();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_NoMatchingSubscribers_When_Not_Subscribed()
|
||||
// Do not increase because the internal enqueue to the target client is not accepted!
|
||||
receivedMessages.AssertReceivedCountEquals(1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Client_Enqueue_Multiple_Clients_Subscribed_Messages_Are_Filtered()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
|
||||
var receiverOne = await testEnvironment.ConnectClient(o => o.WithClientId("One"));
|
||||
await receiverOne.SubscribeAsync("A");
|
||||
var receivedMessagesOne = testEnvironment.CreateApplicationMessageHandler(receiverOne);
|
||||
|
||||
var receiverTwo = await testEnvironment.ConnectClient(o => o.WithClientId("Two"));
|
||||
await receiverTwo.SubscribeAsync("A");
|
||||
var receivedMessagesTwo = testEnvironment.CreateApplicationMessageHandler(receiverTwo);
|
||||
|
||||
var receiverThree = await testEnvironment.ConnectClient(o => o.WithClientId("Three"));
|
||||
await receiverThree.SubscribeAsync("A");
|
||||
var receivedMessagesThree = testEnvironment.CreateApplicationMessageHandler(receiverThree);
|
||||
|
||||
server.InterceptingClientEnqueueAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
if (e.ReceiverClientId.Contains("Two")) e.AcceptEnqueue = false;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await receiver.SubscribeAsync("A");
|
||||
await LongTestDelay();
|
||||
|
||||
// AtLeastOnce is required to get an ACK packet from the server.
|
||||
var publishResult = await sender.PublishStringAsync("B", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.NoMatchingSubscribers, publishResult.ReasonCode);
|
||||
|
||||
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Return_Success_When_Subscribed()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
await receiver.SubscribeAsync("A");
|
||||
|
||||
// AtLeastOnce is required to get an ACK packet from the server.
|
||||
var publishResult = await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, publishResult.ReasonCode);
|
||||
|
||||
Assert.AreEqual(true, publishResult.IsSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Client_Enqueue()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
await receiver.SubscribeAsync("A");
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(1);
|
||||
|
||||
server.InterceptingClientEnqueueAsync += e =>
|
||||
{
|
||||
e.AcceptEnqueue = false;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// Do not increase because the internal enqueue to the target client is not accepted!
|
||||
receivedMessages.AssertReceivedCountEquals(1);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Client_Enqueue_Multiple_Clients_Subscribed_Messages_Are_Filtered()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
|
||||
var receiverOne = await testEnvironment.ConnectClient(o => o.WithClientId("One"));
|
||||
await receiverOne.SubscribeAsync("A");
|
||||
var receivedMessagesOne = testEnvironment.CreateApplicationMessageHandler(receiverOne);
|
||||
|
||||
var receiverTwo = await testEnvironment.ConnectClient(o => o.WithClientId("Two"));
|
||||
await receiverTwo.SubscribeAsync("A");
|
||||
var receivedMessagesTwo = testEnvironment.CreateApplicationMessageHandler(receiverTwo);
|
||||
|
||||
var receiverThree = await testEnvironment.ConnectClient(o => o.WithClientId("Three"));
|
||||
await receiverThree.SubscribeAsync("A");
|
||||
var receivedMessagesThree = testEnvironment.CreateApplicationMessageHandler(receiverThree);
|
||||
|
||||
server.InterceptingClientEnqueueAsync += e =>
|
||||
{
|
||||
if (e.ReceiverClientId.Contains("Two")) e.AcceptEnqueue = false;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await sender.PublishStringAsync("A", "Payload", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessagesOne.AssertReceivedCountEquals(1);
|
||||
receivedMessagesTwo.AssertReceivedCountEquals(0);
|
||||
receivedMessagesThree.AssertReceivedCountEquals(1);
|
||||
}
|
||||
}
|
||||
receivedMessagesOne.AssertReceivedCountEquals(1);
|
||||
receivedMessagesTwo.AssertReceivedCountEquals(0);
|
||||
receivedMessagesThree.AssertReceivedCountEquals(1);
|
||||
}
|
||||
}
|
||||
@@ -11,157 +11,150 @@ using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class QoS_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Fire_Event_On_Client_Acknowledges_QoS_0()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
|
||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
|
||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("A");
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("A");
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("A");
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("A");
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// Must be null because no event should be fired for QoS 0.
|
||||
Assert.IsNull(eventArgs);
|
||||
}
|
||||
// Must be null because no event should be fired for QoS 0.
|
||||
Assert.IsNull(eventArgs);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Event_On_Client_Acknowledges_QoS_1()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
|
||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
ClientAcknowledgedPublishPacketEventArgs eventArgs = null;
|
||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await LongTestDelay();
|
||||
|
||||
await LongTestDelay();
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsNotNull(eventArgs.PublishPacket);
|
||||
Assert.IsNotNull(eventArgs.AcknowledgePacket);
|
||||
Assert.IsTrue(eventArgs.IsCompleted);
|
||||
|
||||
Assert.IsNotNull(eventArgs);
|
||||
Assert.IsNotNull(eventArgs.PublishPacket);
|
||||
Assert.IsNotNull(eventArgs.AcknowledgePacket);
|
||||
Assert.IsTrue(eventArgs.IsCompleted);
|
||||
|
||||
Assert.AreEqual("A", eventArgs.PublishPacket.Topic);
|
||||
}
|
||||
Assert.AreEqual("A", eventArgs.PublishPacket.Topic);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Event_On_Client_Acknowledges_QoS_2()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var eventArgs = new List<ClientAcknowledgedPublishPacketEventArgs>();
|
||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var eventArgs = new List<ClientAcknowledgedPublishPacketEventArgs>();
|
||||
server.ClientAcknowledgedPublishPacketAsync += args =>
|
||||
lock (eventArgs)
|
||||
{
|
||||
lock (eventArgs)
|
||||
{
|
||||
eventArgs.Add(args);
|
||||
}
|
||||
eventArgs.Add(args);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("A", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("A", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, eventArgs.Count);
|
||||
Assert.AreEqual(1, eventArgs.Count);
|
||||
|
||||
var firstEvent = eventArgs[0];
|
||||
var firstEvent = eventArgs[0];
|
||||
|
||||
Assert.IsNotNull(firstEvent);
|
||||
Assert.IsNotNull(firstEvent.PublishPacket);
|
||||
Assert.IsNotNull(firstEvent.AcknowledgePacket);
|
||||
Assert.IsTrue(firstEvent.IsCompleted);
|
||||
Assert.IsNotNull(firstEvent);
|
||||
Assert.IsNotNull(firstEvent.PublishPacket);
|
||||
Assert.IsNotNull(firstEvent.AcknowledgePacket);
|
||||
Assert.IsTrue(firstEvent.IsCompleted);
|
||||
|
||||
Assert.AreEqual("A", firstEvent.PublishPacket.Topic);
|
||||
}
|
||||
Assert.AreEqual("A", firstEvent.PublishPacket.Topic);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Preserve_Message_Order_For_Queued_Messages()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
// Create a session which will contain the messages.
|
||||
var dummyClient = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
|
||||
await dummyClient.SubscribeAsync("#", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
dummyClient.Dispose();
|
||||
// Create a session which will contain the messages.
|
||||
var dummyClient = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
|
||||
await dummyClient.SubscribeAsync("#", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
dummyClient.Dispose();
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
// Now inject messages which are appended to the queue of the client.
|
||||
await server.InjectApplicationMessage("T", "0", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
// Now inject messages which are appended to the queue of the client.
|
||||
await server.InjectApplicationMessage("T", "0", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "2", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "1", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "2", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "1", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "4", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "3", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "4", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "3", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "6", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "5", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "6", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "5", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "8", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "7", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "8", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "7", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await server.InjectApplicationMessage("T", "9", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await server.InjectApplicationMessage("T", "9", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
// Create a new client for the existing message.
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
|
||||
var messages = testEnvironment.CreateApplicationMessageHandler(client);
|
||||
// Create a new client for the existing message.
|
||||
var client = await testEnvironment.ConnectClient(o => o.WithClientId("A").WithCleanSession(false));
|
||||
var messages = testEnvironment.CreateApplicationMessageHandler(client);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
var payloadSequence = messages.GeneratePayloadSequence();
|
||||
Assert.AreEqual("0214365879", payloadSequence);
|
||||
var payloadSequence = messages.GeneratePayloadSequence();
|
||||
Assert.AreEqual("0214365879", payloadSequence);
|
||||
|
||||
// Disconnect and reconnect to make sure that the server will not send the messages twice.
|
||||
await client.DisconnectAsync();
|
||||
await LongTestDelay();
|
||||
await client.ReconnectAsync();
|
||||
await LongTestDelay();
|
||||
// Disconnect and reconnect to make sure that the server will not send the messages twice.
|
||||
await client.DisconnectAsync();
|
||||
await LongTestDelay();
|
||||
await client.ReconnectAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
payloadSequence = messages.GeneratePayloadSequence();
|
||||
Assert.AreEqual("0214365879", payloadSequence);
|
||||
}
|
||||
payloadSequence = messages.GeneratePayloadSequence();
|
||||
Assert.AreEqual("0214365879", payloadSequence);
|
||||
}
|
||||
}
|
||||
@@ -6,42 +6,40 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Retain_As_Published_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Retain_As_Published_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public Task Subscribe_With_Retain_As_Published()
|
||||
{
|
||||
[TestMethod]
|
||||
public Task Subscribe_With_Retain_As_Published()
|
||||
{
|
||||
return ExecuteTest(true);
|
||||
}
|
||||
return ExecuteTest(true);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Subscribe_Without_Retain_As_Published()
|
||||
{
|
||||
return ExecuteTest(false);
|
||||
}
|
||||
[TestMethod]
|
||||
public Task Subscribe_Without_Retain_As_Published()
|
||||
{
|
||||
return ExecuteTest(false);
|
||||
}
|
||||
|
||||
async Task ExecuteTest(bool retainAsPublished)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
async Task ExecuteTest(bool retainAsPublished)
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainAsPublished(retainAsPublished).Build();
|
||||
await client1.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainAsPublished(retainAsPublished).Build();
|
||||
await client1.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
Assert.AreEqual(retainAsPublished, applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.Retain);
|
||||
}
|
||||
}
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
Assert.AreEqual(retainAsPublished, applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.Retain);
|
||||
}
|
||||
}
|
||||
@@ -7,63 +7,61 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Retain_Handling_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Retain_Handling_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public Task Send_At_Subscribe()
|
||||
{
|
||||
[TestMethod]
|
||||
public Task Send_At_Subscribe()
|
||||
{
|
||||
return ExecuteTest(MqttRetainHandling.SendAtSubscribe, 1, 2, 3);
|
||||
}
|
||||
return ExecuteTest(MqttRetainHandling.SendAtSubscribe, 1, 2, 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Do_Not_Send_On_Subscribe()
|
||||
{
|
||||
return ExecuteTest(MqttRetainHandling.DoNotSendOnSubscribe, 0, 1, 1);
|
||||
}
|
||||
[TestMethod]
|
||||
public Task Do_Not_Send_On_Subscribe()
|
||||
{
|
||||
return ExecuteTest(MqttRetainHandling.DoNotSendOnSubscribe, 0, 1, 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Send_At_Subscribe_If_New_Subscription_Only()
|
||||
{
|
||||
return ExecuteTest(MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly, 1, 2, 2);
|
||||
}
|
||||
[TestMethod]
|
||||
public Task Send_At_Subscribe_If_New_Subscription_Only()
|
||||
{
|
||||
return ExecuteTest(MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly, 1, 2, 2);
|
||||
}
|
||||
|
||||
async Task ExecuteTest(
|
||||
MqttRetainHandling retainHandling,
|
||||
int expectedCountAfterSubscribe,
|
||||
int expectedCountAfterSecondPublish,
|
||||
int expectedCountAfterSecondSubscribe)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
async Task ExecuteTest(
|
||||
MqttRetainHandling retainHandling,
|
||||
int expectedCountAfterSubscribe,
|
||||
int expectedCountAfterSecondPublish,
|
||||
int expectedCountAfterSecondSubscribe)
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client2);
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client2);
|
||||
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainHandling(retainHandling).Build();
|
||||
await client2.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").WithRetainHandling(retainHandling).Build();
|
||||
await client2.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSubscribe);
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSubscribe);
|
||||
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondPublish);
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondPublish);
|
||||
|
||||
await client2.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
await client2.SubscribeAsync(topicFilter);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondSubscribe);
|
||||
}
|
||||
}
|
||||
applicationMessageHandler.AssertReceivedCountEquals(expectedCountAfterSecondSubscribe);
|
||||
}
|
||||
}
|
||||
@@ -9,263 +9,243 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Retained_Messages_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Retained_Messages_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Clear_Retained_Message_With_Empty_Payload()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Clear_Retained_Message_With_Empty_Payload()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[0]).WithRetainFlag().Build());
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await Task.Delay(200);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Clear_Retained_Message_With_Null_Payload()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload((byte[])null).WithRetainFlag().Build());
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await Task.Delay(200);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Downgrade_QoS_Level()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
// Add the retained message with QoS 2!
|
||||
await c1.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||
.WithPayload(new byte[3])
|
||||
.WithRetainFlag()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
.Build());
|
||||
|
||||
// The second client uses QoS 1 so a downgrade is required.
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task No_Upgrade_QoS_Level()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
// Add the retained message with QoS 1!
|
||||
await c1.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||
.WithPayload(new byte[3])
|
||||
.WithRetainFlag()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build());
|
||||
|
||||
// The second client uses QoS 2 so an upgrade is expected but according to the MQTT spec this is not supported!
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_No_Retained_Message_After_Subscribe()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained_other").Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_Retained_Message_After_Subscribe()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained").Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
Assert.IsTrue(messageHandler.ReceivedEventArgs.First().ApplicationMessage.Retain);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_Retained_Messages_From_Higher_Qos_Level()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
// Upload retained message.
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
await c1.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||
.WithPayload(new byte[1])
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.WithRetainFlag()
|
||||
.Build());
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
// Subscribe using a new client.
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await Task.Delay(200);
|
||||
// Using QoS 2 will lead to 1 instead because the publish was made with QoS level 1 (see 3.8.4 SUBSCRIBE Actions)!
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Retained_Messages_Flow()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var retainedMessage = new MqttApplicationMessageBuilder().WithTopic("r").WithPayload("r").WithRetainFlag().Build();
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await c1.PublishAsync(retainedMessage);
|
||||
await c1.DisconnectAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
await c2.UnsubscribeAsync("r");
|
||||
await Task.Delay(100);
|
||||
messageHandler.AssertReceivedCountEquals(i);
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[0]).WithRetainFlag().Build());
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await Task.Delay(200);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
await c2.SubscribeAsync("r");
|
||||
await Task.Delay(100);
|
||||
messageHandler.AssertReceivedCountEquals(i + 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Clear_Retained_Message_With_Null_Payload()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
await c2.DisconnectAsync();
|
||||
}
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Retained_Messages_Supported_V3()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload((byte[])null).WithRetainFlag().Build());
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
var connectResult = await client.ConnectAsync(
|
||||
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build());
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
Assert.IsTrue(connectResult.RetainAvailable);
|
||||
}
|
||||
|
||||
await Task.Delay(200);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce });
|
||||
await Task.Delay(500);
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Retained_Messages_Supported_V5()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
}
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(
|
||||
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build());
|
||||
|
||||
[TestMethod]
|
||||
public async Task Downgrade_QoS_Level()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
// Add the retained message with QoS 2!
|
||||
await c1.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||
.WithPayload(new byte[3])
|
||||
.WithRetainFlag()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
.Build());
|
||||
|
||||
// The second client uses QoS 1 so a downgrade is required.
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task No_Upgrade_QoS_Level()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
// Add the retained message with QoS 1!
|
||||
await c1.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||
.WithPayload(new byte[3])
|
||||
.WithRetainFlag()
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.Build());
|
||||
|
||||
// The second client uses QoS 2 so an upgrade is expected but according to the MQTT spec this is not supported!
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
Assert.AreEqual(MqttQualityOfServiceLevel.AtLeastOnce, messageHandler.ReceivedEventArgs.First().ApplicationMessage.QualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_No_Retained_Message_After_Subscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained_other").Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(0);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_Retained_Message_After_Subscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
await c1.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("retained").WithPayload(new byte[3]).WithRetainFlag().Build());
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await c2.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("retained").Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
Assert.IsTrue(messageHandler.ReceivedEventArgs.First().ApplicationMessage.Retain);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Receive_Retained_Messages_From_Higher_Qos_Level()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
// Upload retained message.
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
await c1.PublishAsync(
|
||||
new MqttApplicationMessageBuilder().WithTopic("retained")
|
||||
.WithPayload(new byte[1])
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.WithRetainFlag()
|
||||
.Build());
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
// Subscribe using a new client.
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await Task.Delay(200);
|
||||
// Using QoS 2 will lead to 1 instead because the publish was made with QoS level 1 (see 3.8.4 SUBSCRIBE Actions)!
|
||||
await c2.SubscribeAsync(new MqttTopicFilter { Topic = "retained", QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce });
|
||||
await Task.Delay(500);
|
||||
|
||||
messageHandler.AssertReceivedCountEquals(1);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Retained_Messages_Flow()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var retainedMessage = new MqttApplicationMessageBuilder().WithTopic("r").WithPayload("r").WithRetainFlag().Build();
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
var messageHandler = testEnvironment.CreateApplicationMessageHandler(c2);
|
||||
|
||||
await c1.PublishAsync(retainedMessage);
|
||||
await c1.DisconnectAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
await c2.UnsubscribeAsync("r");
|
||||
await Task.Delay(100);
|
||||
messageHandler.AssertReceivedCountEquals(i);
|
||||
|
||||
await c2.SubscribeAsync("r");
|
||||
await Task.Delay(100);
|
||||
messageHandler.AssertReceivedCountEquals(i + 1);
|
||||
}
|
||||
|
||||
await c2.DisconnectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Retained_Messages_Supported_V3()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var connectResult = await client.ConnectAsync(
|
||||
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build());
|
||||
|
||||
Assert.IsTrue(connectResult.RetainAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Retained_Messages_Supported_V5()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(
|
||||
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build());
|
||||
|
||||
Assert.IsTrue(connectResult.RetainAvailable);
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(connectResult.RetainAvailable);
|
||||
}
|
||||
}
|
||||
@@ -10,161 +10,151 @@ using MQTTnet.Exceptions;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Security_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Security_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Do_Not_Affect_Authorized_Clients()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Do_Not_Affect_Authorized_Clients()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var publishedApplicationMessages = new List<MqttApplicationMessage>();
|
||||
testEnvironment.Server.InterceptingPublishAsync += eventArgs =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
lock (publishedApplicationMessages)
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var publishedApplicationMessages = new List<MqttApplicationMessage>();
|
||||
testEnvironment.Server.InterceptingPublishAsync += eventArgs =>
|
||||
{
|
||||
lock (publishedApplicationMessages)
|
||||
{
|
||||
publishedApplicationMessages.Add(eventArgs.ApplicationMessage);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
|
||||
{
|
||||
if (eventArgs.UserName == "SECRET")
|
||||
{
|
||||
eventArgs.ReasonCode = MqttConnectReasonCode.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
using (var validClient = testEnvironment.CreateClient())
|
||||
{
|
||||
await validClient.ConnectAsync(
|
||||
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("localhost", testEnvironment.ServerPort)
|
||||
.WithCredentials("SECRET")
|
||||
.WithClientId("CLIENT")
|
||||
.Build());
|
||||
|
||||
await validClient.PublishStringAsync("HELLO 1");
|
||||
|
||||
// The following code tries to connect a new client with the same client ID but invalid
|
||||
// credentials. This should block the second client but keep the first one up and running.
|
||||
try
|
||||
{
|
||||
using (var invalidClient = testEnvironment.CreateClient())
|
||||
{
|
||||
await invalidClient.ConnectAsync(
|
||||
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("localhost", testEnvironment.ServerPort)
|
||||
.WithCredentials("???")
|
||||
.WithClientId("CLIENT")
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors from the second client.
|
||||
}
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
await validClient.PublishStringAsync("HELLO 2");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
await validClient.PublishStringAsync("HELLO 3");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(3, publishedApplicationMessages.Count);
|
||||
Assert.AreEqual(1, testEnvironment.Server.GetClientsAsync().GetAwaiter().GetResult().Count);
|
||||
}
|
||||
publishedApplicationMessages.Add(eventArgs.ApplicationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_Password()
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
testEnvironment.Server.ValidatingConnectionAsync += eventArgs =>
|
||||
{
|
||||
return TestCredentials("UserName", "x");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_UserName()
|
||||
{
|
||||
return TestCredentials("x", "Password1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_UserName_And_Password()
|
||||
{
|
||||
return TestCredentials("x", "x");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_Username_Null_Password_Empty()
|
||||
{
|
||||
string username = null;
|
||||
var password = string.Empty;
|
||||
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
if (eventArgs.UserName == "SECRET")
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(username, password).Build();
|
||||
|
||||
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions));
|
||||
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
|
||||
Assert.AreEqual("Error while authenticating. If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false);
|
||||
eventArgs.ReasonCode = MqttConnectReasonCode.Success;
|
||||
}
|
||||
}
|
||||
|
||||
async Task TestCredentials(string userName, string password)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
else
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (e.UserName != "UserName1")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
if (e.Password != "Password1")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(userName, password).Build();
|
||||
|
||||
var response = await client.ConnectAsync(clientOptions);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, response.ResultCode);
|
||||
eventArgs.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
using var validClient = testEnvironment.CreateClient();
|
||||
await validClient.ConnectAsync(
|
||||
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("localhost", testEnvironment.ServerPort)
|
||||
.WithCredentials("SECRET")
|
||||
.WithClientId("CLIENT")
|
||||
.Build());
|
||||
|
||||
await validClient.PublishStringAsync("HELLO 1");
|
||||
|
||||
// The following code tries to connect a new client with the same client ID but invalid
|
||||
// credentials. This should block the second client but keep the first one up and running.
|
||||
try
|
||||
{
|
||||
using var invalidClient = testEnvironment.CreateClient();
|
||||
await invalidClient.ConnectAsync(
|
||||
testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("localhost", testEnvironment.ServerPort)
|
||||
.WithCredentials("???")
|
||||
.WithClientId("CLIENT")
|
||||
.Build());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors from the second client.
|
||||
}
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
await validClient.PublishStringAsync("HELLO 2");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
await validClient.PublishStringAsync("HELLO 3");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(3, publishedApplicationMessages.Count);
|
||||
Assert.AreEqual(1, testEnvironment.Server.GetClientsAsync().GetAwaiter().GetResult().Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_Password()
|
||||
{
|
||||
return TestCredentials("UserName", "x");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_UserName()
|
||||
{
|
||||
return TestCredentials("x", "Password1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public Task Handle_Wrong_UserName_And_Password()
|
||||
{
|
||||
return TestCredentials("x", "x");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_Username_Null_Password_Empty()
|
||||
{
|
||||
string username = null;
|
||||
var password = string.Empty;
|
||||
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(username, password).Build();
|
||||
|
||||
var ex = await Assert.ThrowsExceptionAsync<MqttConnectingFailedException>(async () => await client.ConnectAsync(clientOptions).ConfigureAwait(false));
|
||||
Assert.IsInstanceOfType(ex.InnerException, typeof(MqttProtocolViolationException));
|
||||
Assert.AreEqual("Error while authenticating. If the User Name Flag is set to 0, the Password Flag MUST be set to 0 [MQTT-3.1.2-22].", ex.Message, false);
|
||||
}
|
||||
|
||||
async Task TestCredentials(string userName, string password)
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (e.UserName != "UserName1")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
if (e.Password != "Password1")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).WithCredentials(userName, password).Build();
|
||||
|
||||
var response = await client.ConnectAsync(clientOptions);
|
||||
Assert.AreEqual(MqttClientConnectResultCode.BadUserNameOrPassword, response.ResultCode);
|
||||
}
|
||||
}
|
||||
@@ -10,32 +10,31 @@ using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Server_Reference_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_With_Reference_Server()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
|
||||
e.ServerReference = "new_server";
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.ServerMoved;
|
||||
e.ServerReference = "new_server";
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
var response = await client.ConnectAsync(
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var response = await client.ConnectAsync(
|
||||
new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500).WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.AreEqual(MqttClientConnectResultCode.ServerMoved, response.ResultCode);
|
||||
Assert.AreEqual("new_server", response.ServerReference);
|
||||
}
|
||||
Assert.AreEqual(MqttClientConnectResultCode.ServerMoved, response.ResultCode);
|
||||
Assert.AreEqual("new_server", response.ServerReference);
|
||||
}
|
||||
}
|
||||
@@ -13,377 +13,357 @@ using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Session_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Session_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Clean_Session_Persistence()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Clean_Session_Persistence()
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
// Create server with persistent sessions enabled
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
const string clientId = "Client1";
|
||||
|
||||
// Create client with clean session and long session expiry interval
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient(
|
||||
o => o.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||
.WithCleanSession() // start and end with clean session
|
||||
.WithClientId(clientId)
|
||||
.Build());
|
||||
|
||||
// Disconnect; empty session should be removed from server
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
|
||||
// Simulate some time delay between connections
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Reconnect the same client ID without clean session
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options = testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||
.WithCleanSession(false) // see if there is a session
|
||||
.WithClientId(clientId)
|
||||
.Build();
|
||||
|
||||
|
||||
var result = await client2.ConnectAsync(options).ConfigureAwait(false);
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
|
||||
// Session should NOT be present for MQTT v311 and initial CleanSession == true
|
||||
|
||||
Assert.IsTrue(!result.IsSessionPresent, "Session present");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Do_Not_Use_Expired_Session()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(o => o.WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3));
|
||||
|
||||
// Kill the client connection and ensure that the session will stay there but is expired after 3 seconds!
|
||||
c1.Dispose();
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(6));
|
||||
|
||||
c1 = testEnvironment.CreateClient();
|
||||
var options = testEnvironment.CreateDefaultClientOptionsBuilder().WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3).Build();
|
||||
|
||||
var connectResult = await c1.ConnectAsync(options);
|
||||
Assert.AreEqual(false, connectResult.IsSessionPresent);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Deleted_Event()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
// Arrange client and server.
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions(false));
|
||||
|
||||
var deletedEventFired = false;
|
||||
server.SessionDeletedAsync += _ =>
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
// Create server with persistent sessions enabled
|
||||
deletedEventFired = true;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
const string clientId = "Client1";
|
||||
// Act: Disconnect the client -> Event must be fired.
|
||||
await client.DisconnectAsync();
|
||||
|
||||
// Create client with clean session and long session expiry interval
|
||||
await LongTestDelay();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient(
|
||||
o => o.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||
.WithCleanSession() // start and end with clean session
|
||||
.WithClientId(clientId)
|
||||
.Build());
|
||||
// Assert that the event was fired properly.
|
||||
Assert.IsTrue(deletedEventFired);
|
||||
}
|
||||
|
||||
// Disconnect; empty session should be removed from server
|
||||
[TestMethod]
|
||||
public async Task Get_Session_Items_In_Status()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
await client1.DisconnectAsync();
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
// Don't validate anything. Just set some session items.
|
||||
e.SessionItems["can_subscribe_x"] = true;
|
||||
e.SessionItems["default_payload"] = "Hello World";
|
||||
|
||||
// Simulate some time delay between connections
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await Task.Delay(1000);
|
||||
await testEnvironment.ConnectClient();
|
||||
|
||||
// Reconnect the same client ID without clean session
|
||||
var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
|
||||
var session = sessionStatus.First();
|
||||
|
||||
var client2 = testEnvironment.CreateClient();
|
||||
var options = testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithSessionExpiryInterval(9999) // not relevant for v311 but testing impact
|
||||
.WithCleanSession(false) // see if there is a session
|
||||
.WithClientId(clientId)
|
||||
.Build();
|
||||
Assert.AreEqual(true, session.Items["can_subscribe_x"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(MqttProtocolVersion.V310)]
|
||||
[DataRow(MqttProtocolVersion.V311)]
|
||||
[DataRow(MqttProtocolVersion.V500)]
|
||||
public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion)
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var result = await client2.ConnectAsync(options).ConfigureAwait(false);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
await client2.DisconnectAsync();
|
||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(10)).WithProtocolVersion(protocolVersion);
|
||||
|
||||
// Session should NOT be present for MQTT v311 and initial CleanSession == true
|
||||
var hasReceive = false;
|
||||
|
||||
Assert.IsTrue(!result.IsSessionPresent, "Session present");
|
||||
}
|
||||
// Try to connect 50 clients at the same time.
|
||||
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(_ => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
|
||||
|
||||
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, connectedClients.Count);
|
||||
|
||||
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
||||
var sendClient = await testEnvironment.ConnectClient(option2);
|
||||
await sendClient.PublishStringAsync("aaa", "1");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(true, hasReceive);
|
||||
return;
|
||||
|
||||
void OnReceive()
|
||||
{
|
||||
hasReceive = true;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Do_Not_Use_Expired_Session()
|
||||
[TestMethod]
|
||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
||||
public async Task Retry_If_Not_PubAck(MqttQualityOfServiceLevel qos)
|
||||
{
|
||||
long count = 0;
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
var publisher = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscriber = await testEnvironment.ConnectClient(o => o.WithClientId(qos.ToString()).WithCleanSession(false));
|
||||
|
||||
subscriber.ApplicationMessageReceivedAsync += c =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
c.AutoAcknowledge = false;
|
||||
++count;
|
||||
Console.WriteLine("process");
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(o => o.WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3));
|
||||
await subscriber.SubscribeAsync("#", qos);
|
||||
|
||||
// Kill the client connection and ensure that the session will stay there but is expired after 3 seconds!
|
||||
c1.Dispose();
|
||||
var pub = publisher.PublishStringAsync("a", null, qos);
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(6));
|
||||
await Task.Delay(100);
|
||||
await subscriber.DisconnectAsync();
|
||||
await subscriber.ConnectAsync(subscriber.Options);
|
||||
await Task.Delay(100);
|
||||
|
||||
c1 = testEnvironment.CreateClient();
|
||||
var options = testEnvironment.CreateDefaultClientOptionsBuilder().WithClientId("Client1").WithCleanSession(false).WithSessionExpiryInterval(3).Build();
|
||||
var res = await pub;
|
||||
|
||||
var connectResult = await c1.ConnectAsync(options);
|
||||
Assert.AreEqual(false, connectResult.IsSessionPresent);
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, res.ReasonCode);
|
||||
Assert.AreEqual(2, count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Deleted_Event()
|
||||
[TestMethod]
|
||||
public async Task Session_Takeover()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var options = new MqttClientOptionsBuilder().WithCleanSession(false)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500) // Disconnect reason is only available in MQTT 5+
|
||||
.WithClientId("a");
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient(options);
|
||||
await Task.Delay(500);
|
||||
|
||||
var disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
|
||||
client1.DisconnectedAsync += c =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
// Arrange client and server.
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions(false));
|
||||
disconnectReason = c.Reason;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var deletedEventFired = false;
|
||||
server.SessionDeletedAsync += e =>
|
||||
var client2 = await testEnvironment.ConnectClient(options);
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsFalse(client1.IsConnected);
|
||||
Assert.IsTrue(client2.IsConnected);
|
||||
|
||||
Assert.AreEqual(MqttClientDisconnectReason.SessionTakenOver, disconnectReason);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Set_Session_Item()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
// Don't validate anything. Just set some session items.
|
||||
e.SessionItems["can_subscribe_x"] = true;
|
||||
e.SessionItems["default_payload"] = "Hello World";
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
if (e.TopicFilter.Topic == "x")
|
||||
{
|
||||
if (e.SessionItems["can_subscribe_x"] as bool? == false)
|
||||
{
|
||||
deletedEventFired = true;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
// Act: Disconnect the client -> Event must be fired.
|
||||
await client.DisconnectAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
// Assert that the event was fired properly.
|
||||
Assert.IsTrue(deletedEventFired);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Get_Session_Items_In_Status()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
// Don't validate anything. Just set some session items.
|
||||
e.SessionItems["can_subscribe_x"] = true;
|
||||
e.SessionItems["default_payload"] = "Hello World";
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await testEnvironment.ConnectClient();
|
||||
|
||||
var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
|
||||
var session = sessionStatus.First();
|
||||
|
||||
Assert.AreEqual(true, session.Items["can_subscribe_x"]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(MqttProtocolVersion.V310)]
|
||||
[DataRow(MqttProtocolVersion.V311)]
|
||||
[DataRow(MqttProtocolVersion.V500)]
|
||||
public async Task Handle_Parallel_Connection_Attempts(MqttProtocolVersion protocolVersion)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var options = new MqttClientOptionsBuilder().WithClientId("1").WithTimeout(TimeSpan.FromSeconds(10)).WithProtocolVersion(protocolVersion);
|
||||
|
||||
var hasReceive = false;
|
||||
void OnReceive()
|
||||
{
|
||||
hasReceive = true;
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
|
||||
}
|
||||
|
||||
// Try to connect 50 clients at the same time.
|
||||
var clients = await Task.WhenAll(Enumerable.Range(0, 50).Select(i => ConnectAndSubscribe(testEnvironment, options, OnReceive)));
|
||||
|
||||
var connectedClients = clients.Where(c => c?.TryPingAsync().GetAwaiter().GetResult() == true).ToList();
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, connectedClients.Count);
|
||||
|
||||
var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
|
||||
var sendClient = await testEnvironment.ConnectClient(option2);
|
||||
await sendClient.PublishStringAsync("aaa", "1");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(true, hasReceive);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
||||
public async Task Retry_If_Not_PubAck(MqttQualityOfServiceLevel qos)
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
long count = 0;
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var publisher = await testEnvironment.ConnectClient();
|
||||
string receivedPayload = null;
|
||||
|
||||
var subscriber = await testEnvironment.ConnectClient(o => o.WithClientId(qos.ToString()).WithCleanSession(false));
|
||||
|
||||
subscriber.ApplicationMessageReceivedAsync += c =>
|
||||
{
|
||||
c.AutoAcknowledge = false;
|
||||
++count;
|
||||
Console.WriteLine("process");
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await subscriber.SubscribeAsync("#", qos);
|
||||
|
||||
var pub = publisher.PublishStringAsync("a", null, qos);
|
||||
|
||||
await Task.Delay(100);
|
||||
await subscriber.DisconnectAsync();
|
||||
await subscriber.ConnectAsync(subscriber.Options);
|
||||
await Task.Delay(100);
|
||||
|
||||
var res = await pub;
|
||||
|
||||
Assert.AreEqual(MqttClientPublishReasonCode.Success, res.ReasonCode);
|
||||
Assert.AreEqual(2, count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Session_Takeover()
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var options = new MqttClientOptionsBuilder().WithCleanSession(false)
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500) // Disconnect reason is only available in MQTT 5+
|
||||
.WithClientId("a");
|
||||
var subscribeResult = await client.SubscribeAsync("x");
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient(options);
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
|
||||
|
||||
var disconnectReason = MqttClientDisconnectReason.NormalDisconnection;
|
||||
client1.DisconnectedAsync += c =>
|
||||
{
|
||||
disconnectReason = c.Reason;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("x");
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient(options);
|
||||
await Task.Delay(500);
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.IsFalse(client1.IsConnected);
|
||||
Assert.IsTrue(client2.IsConnected);
|
||||
Assert.AreEqual("Hello World", receivedPayload);
|
||||
}
|
||||
|
||||
Assert.AreEqual(MqttClientDisconnectReason.SessionTakenOver, disconnectReason);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Use_Clean_Session()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Set_Session_Item()
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).WithCleanSession().Build());
|
||||
|
||||
// Create the session including the subscription.
|
||||
var client1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a").WithCleanSession(false));
|
||||
await client1.SubscribeAsync("x");
|
||||
await client1.DisconnectAsync();
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsFalse(connectResult.IsSessionPresent);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Will_Message_Do_Not_Send_On_Takeover()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
// C1 will receive the last will!
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
server.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
// Don't validate anything. Just set some session items.
|
||||
e.SessionItems["can_subscribe_x"] = true;
|
||||
e.SessionItems["default_payload"] = "Hello World";
|
||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
// C2 has the last will defined.
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
if (e.TopicFilter.Topic == "x")
|
||||
{
|
||||
if (e.SessionItems["can_subscribe_x"] as bool? == false)
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.ImplementationSpecificError;
|
||||
}
|
||||
}
|
||||
await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
// C3 will do the connection takeover.
|
||||
await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
server.InterceptingPublishAsync += e =>
|
||||
{
|
||||
e.ApplicationMessage.PayloadSegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes(e.SessionItems["default_payload"] as string ?? string.Empty));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
await Task.Delay(1000);
|
||||
|
||||
string receivedPayload = null;
|
||||
Assert.AreEqual(0, receivedMessagesCount);
|
||||
}
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var subscribeResult = await client.SubscribeAsync("x");
|
||||
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.PublishStringAsync("x");
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.AreEqual("Hello World", receivedPayload);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_Clean_Session()
|
||||
static async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
var client = await testEnvironment.ConnectClient(options).ConfigureAwait(false);
|
||||
|
||||
client.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
onReceive();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).WithCleanSession().Build());
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
|
||||
|
||||
// Create the session including the subscription.
|
||||
var client1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a").WithCleanSession(false));
|
||||
await client1.SubscribeAsync("x");
|
||||
await client1.DisconnectAsync();
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsFalse(connectResult.IsSessionPresent);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Will_Message_Do_Not_Send_On_Takeover()
|
||||
catch
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
// C1 will receive the last will!
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
|
||||
// C2 has the last will defined.
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithClientId("WillOwner");
|
||||
|
||||
await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
// C3 will do the connection takeover.
|
||||
await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.AreEqual(0, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
static async Task<IMqttClient> ConnectAndSubscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = await testEnvironment.ConnectClient(options).ConfigureAwait(false);
|
||||
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
onReceive();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
using (var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
|
||||
{
|
||||
await client.SubscribeAsync("aaa", MqttQualityOfServiceLevel.AtMostOnce, timeout.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,43 +7,39 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Shared_Subscriptions_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Shared_Subscriptions_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Shared_Subscriptions_Not_Supported()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Shared_Subscriptions_Not_Supported()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsFalse(connectResult.SharedSubscriptionAvailable);
|
||||
}
|
||||
}
|
||||
Assert.IsFalse(connectResult.SharedSubscriptionAvailable);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscription_Of_Shared_Subscription_Is_Denied()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
[TestMethod]
|
||||
public async Task Subscription_Of_Shared_Subscription_Is_Denied()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
var client = testEnvironment.CreateClient();
|
||||
await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
var subscribeResult = await client.SubscribeAsync("$share/A");
|
||||
var subscribeResult = await client.SubscribeAsync("$share/A");
|
||||
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.SharedSubscriptionsNotSupported, subscribeResult.Items.First().ResultCode);
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.SharedSubscriptionsNotSupported, subscribeResult.Items.First().ResultCode);
|
||||
}
|
||||
}
|
||||
@@ -12,189 +12,178 @@ using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Status_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Client()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
|
||||
|
||||
await Task.Delay(1000);
|
||||
await Task.Delay(1000);
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
|
||||
|
||||
await clientStatus.First().DisconnectAsync();
|
||||
await clientStatus[0].DisconnectAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsFalse(c1.IsConnected);
|
||||
Assert.IsFalse(c1.IsConnected);
|
||||
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
}
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Keep_Persistent_Session_Version311()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V311));
|
||||
var c2 = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V311));
|
||||
var c1 = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V311));
|
||||
var c2 = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V311));
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
await c1.DisconnectAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var sessionStatus = await server.GetSessionsAsync();
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
|
||||
await c2.DisconnectAsync();
|
||||
await c2.DisconnectAsync();
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
sessionStatus = await server.GetSessionsAsync();
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
}
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Keep_Persistent_Session_Version500()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
var server = await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var c2 = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var c1 = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder().WithClientId("client1").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var c2 = await testEnvironment.ConnectClient(
|
||||
new MqttClientOptionsBuilder().WithClientId("client2").WithCleanSession(false).WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
// The session expiry interval is mandatory for MQTT5.0.0 in order keep session!
|
||||
await c1.DisconnectAsync(sessionExpiryInterval: 60);
|
||||
// The session expiry interval is mandatory for MQTT5.0.0 in order keep session!
|
||||
await c1.DisconnectAsync(sessionExpiryInterval: 60);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var sessionStatus = await server.GetSessionsAsync();
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
|
||||
// The session expiry interval is mandatory for MQTT5.0.0 in order keep session!
|
||||
await c2.DisconnectAsync(sessionExpiryInterval: 60);
|
||||
// The session expiry interval is mandatory for MQTT5.0.0 in order keep session!
|
||||
await c2.DisconnectAsync(sessionExpiryInterval: 60);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
sessionStatus = await server.GetSessionsAsync();
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
}
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Show_Client_And_Session_Statistics()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
|
||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client2"));
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client1"));
|
||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("client2"));
|
||||
|
||||
await Task.Delay(500);
|
||||
await Task.Delay(500);
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var sessionStatus = await server.GetSessionsAsync();
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
var sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(2, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
Assert.AreEqual(2, clientStatus.Count);
|
||||
Assert.AreEqual(2, sessionStatus.Count);
|
||||
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c2.Options.ClientId));
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c1.Options.ClientId));
|
||||
Assert.IsTrue(clientStatus.Any(s => s.Id == c2.Options.ClientId));
|
||||
|
||||
await c1.DisconnectAsync();
|
||||
await c2.DisconnectAsync();
|
||||
await c1.DisconnectAsync();
|
||||
await c2.DisconnectAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
await Task.Delay(500);
|
||||
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
sessionStatus = await server.GetSessionsAsync();
|
||||
clientStatus = await server.GetClientsAsync();
|
||||
sessionStatus = await server.GetSessionsAsync();
|
||||
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
Assert.AreEqual(0, sessionStatus.Count);
|
||||
}
|
||||
Assert.AreEqual(0, clientStatus.Count);
|
||||
Assert.AreEqual(0, sessionStatus.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Track_Sent_Application_Messages()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
for (var i = 1; i < 25; i++)
|
||||
{
|
||||
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
|
||||
await c1.PublishStringAsync("a");
|
||||
await Task.Delay(50);
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
|
||||
for (var i = 1; i < 25; i++)
|
||||
{
|
||||
await c1.PublishStringAsync("a");
|
||||
await Task.Delay(50);
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount);
|
||||
Assert.AreEqual(0, clientStatus.First().ReceivedApplicationMessagesCount);
|
||||
}
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
Assert.AreEqual(i, clientStatus[0].SentApplicationMessagesCount);
|
||||
Assert.AreEqual(0, clientStatus[0].ReceivedApplicationMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Track_Sent_Packets()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithNoKeepAlive());
|
||||
|
||||
for (var i = 1; i < 25; i++)
|
||||
{
|
||||
var server = await testEnvironment.StartServer(new MqttServerOptionsBuilder().WithPersistentSessions());
|
||||
// At most once will send one packet to the client and the server will reply
|
||||
// with an additional ACK packet.
|
||||
await c1.PublishStringAsync("a", string.Empty, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithNoKeepAlive());
|
||||
await Task.Delay(500);
|
||||
|
||||
for (var i = 1; i < 25; i++)
|
||||
{
|
||||
// At most once will send one packet to the client and the server will reply
|
||||
// with an additional ACK packet.
|
||||
await c1.PublishStringAsync("a", string.Empty, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(i, clientStatus[0].SentApplicationMessagesCount, "SAMC invalid!");
|
||||
|
||||
var clientStatus = await server.GetClientsAsync();
|
||||
// + 1 because CONNECT is also counted.
|
||||
Assert.AreEqual(i + 1, clientStatus[0].SentPacketsCount, "SPC invalid!");
|
||||
|
||||
Assert.AreEqual(i, clientStatus.First().SentApplicationMessagesCount, "SAMC invalid!");
|
||||
|
||||
// + 1 because CONNECT is also counted.
|
||||
Assert.AreEqual(i + 1, clientStatus.First().SentPacketsCount, "SPC invalid!");
|
||||
|
||||
// +2 because ConnACK + PubAck package is already counted.
|
||||
Assert.AreEqual(i + 2, clientStatus.First().ReceivedPacketsCount, "RPC invalid!");
|
||||
}
|
||||
// +2 because ConnACK + PubAck package is already counted.
|
||||
Assert.AreEqual(i + 2, clientStatus[0].ReceivedPacketsCount, "RPC invalid!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,420 +15,396 @@ using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Helpers;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Subscribe_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Subscribe_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
[DataRow("A", "A", true)]
|
||||
[DataRow("A", "B", false)]
|
||||
[DataRow("A", "#", true)]
|
||||
[DataRow("A", "+", true)]
|
||||
[DataRow("A/B", "A/B", true)]
|
||||
[DataRow("A/B", "A/+", true)]
|
||||
[DataRow("A/B", "A/#", true)]
|
||||
[DataRow("A/B/C", "A/B/C", true)]
|
||||
[DataRow("A/B/C", "A/+/C", true)]
|
||||
[DataRow("A/B/C", "A/+/+", true)]
|
||||
[DataRow("A/B/C", "A/+/#", true)]
|
||||
[DataRow("A/B/C/D", "A/B/C/D", true)]
|
||||
[DataRow("A/B/C/D", "A/+/C/+", true)]
|
||||
[DataRow("A/B/C/D", "A/+/C/#", true)]
|
||||
[DataRow("A/B/C", "A/B/+", true)]
|
||||
[DataRow("A/B1/B2/C", "A/+/C", false)]
|
||||
public async Task Subscription_Roundtrip(string topic, string filter, bool shouldWork)
|
||||
{
|
||||
[TestMethod]
|
||||
[DataRow("A", "A", true)]
|
||||
[DataRow("A", "B", false)]
|
||||
[DataRow("A", "#", true)]
|
||||
[DataRow("A", "+", true)]
|
||||
[DataRow("A/B", "A/B", true)]
|
||||
[DataRow("A/B", "A/+", true)]
|
||||
[DataRow("A/B", "A/#", true)]
|
||||
[DataRow("A/B/C", "A/B/C", true)]
|
||||
[DataRow("A/B/C", "A/+/C", true)]
|
||||
[DataRow("A/B/C", "A/+/+", true)]
|
||||
[DataRow("A/B/C", "A/+/#", true)]
|
||||
[DataRow("A/B/C/D", "A/B/C/D", true)]
|
||||
[DataRow("A/B/C/D", "A/+/C/+", true)]
|
||||
[DataRow("A/B/C/D", "A/+/C/#", true)]
|
||||
[DataRow("A/B/C", "A/B/+", true)]
|
||||
[DataRow("A/B1/B2/C", "A/+/C", false)]
|
||||
public async Task Subscription_Roundtrip(string topic, string filter, bool shouldWork)
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer().ConfigureAwait(false);
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
await receiver.SubscribeAsync(filter).ConfigureAwait(false);
|
||||
var receivedMessages = receiver.TrackReceivedMessages();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
await sender.PublishStringAsync(topic, "PAYLOAD").ConfigureAwait(false);
|
||||
|
||||
await LongTestDelay().ConfigureAwait(false);
|
||||
|
||||
if (shouldWork)
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer().ConfigureAwait(false);
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
await receiver.SubscribeAsync(filter).ConfigureAwait(false);
|
||||
var receivedMessages = receiver.TrackReceivedMessages();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
await sender.PublishStringAsync(topic, "PAYLOAD").ConfigureAwait(false);
|
||||
|
||||
await LongTestDelay().ConfigureAwait(false);
|
||||
|
||||
if (shouldWork)
|
||||
{
|
||||
Assert.AreEqual(1, receivedMessages.Count, message: "The filter should work!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(0, receivedMessages.Count, message: "The filter should not work!");
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(1, receivedMessages.Count, message: "The filter should work!");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deny_Invalid_Topic()
|
||||
else
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
if (e.TopicFilter.Topic == "not_allowed_topic")
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscribeResult = await client.SubscribeAsync("allowed_topic");
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
|
||||
|
||||
subscribeResult = await client.SubscribeAsync("not_allowed_topic");
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.TopicFilterInvalid, subscribeResult.Items.First().ResultCode);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Subscribe_With_User_Properties()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
InterceptingSubscriptionEventArgs eventArgs = null;
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
|
||||
await client.SubscribeAsync(subscribeOptions);
|
||||
|
||||
CollectionAssert.AreEqual(subscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Subscribing()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
// The client will be disconnect directly after subscribing!
|
||||
server.ClientSubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.SubscribeAsync("#");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Message_After_Subscription()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
server.InjectApplicationMessage(new InjectedMqttApplicationMessage(new MqttApplicationMessageBuilder().WithTopic("test_topic").Build()));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(client);
|
||||
|
||||
await client.SubscribeAsync("test_topic");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Subscription()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
// Set the topic to "a" regards what the client wants to subscribe.
|
||||
e.TopicFilter.Topic = "a";
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var topicAReceived = false;
|
||||
var topicBReceived = false;
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
if (e.ApplicationMessage.Topic == "a")
|
||||
{
|
||||
topicAReceived = true;
|
||||
}
|
||||
else if (e.ApplicationMessage.Topic == "b")
|
||||
{
|
||||
topicBReceived = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.SubscribeAsync("b");
|
||||
|
||||
await client.PublishStringAsync("a");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsTrue(topicAReceived);
|
||||
Assert.IsFalse(topicBReceived);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Response_Contains_Equal_Reason_Codes()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a")
|
||||
.WithTopicFilter("b", MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.WithTopicFilter("c", MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
.WithTopicFilter("d")
|
||||
.Build();
|
||||
|
||||
var response = await client.SubscribeAsync(subscribeOptions);
|
||||
|
||||
Assert.AreEqual(subscribeOptions.TopicFilters.Count, response.Items.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Lots_In_Multiple_Requests()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
var so = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(i.ToString()).Build();
|
||||
|
||||
await c1.SubscribeAsync(so).ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
messageBuilder.WithTopic(i.ToString());
|
||||
|
||||
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessagesCount == 500, 5000);
|
||||
|
||||
Assert.AreEqual(500, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Lots_In_Single_Request()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
optionsBuilder.WithTopicFilter(i.ToString());
|
||||
}
|
||||
|
||||
await c1.SubscribeAsync(optionsBuilder.Build()).ConfigureAwait(false);
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
messageBuilder.WithTopic(i.ToString());
|
||||
|
||||
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessagesCount == 500, TimeSpan.FromSeconds(20));
|
||||
|
||||
Assert.AreEqual(500, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Multiple_In_Multiple_Request()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("b").Build());
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("c").Build());
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c2.PublishStringAsync("a");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 1);
|
||||
|
||||
await c2.PublishStringAsync("b");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 2);
|
||||
|
||||
await c2.PublishStringAsync("c");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 3);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Multiple_In_Single_Request()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c2.PublishStringAsync("a");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 1);
|
||||
|
||||
await c2.PublishStringAsync("b");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 2);
|
||||
|
||||
await c2.PublishStringAsync("c");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 3);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Unsubscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c1"));
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce).Build();
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(0, receivedMessagesCount);
|
||||
|
||||
var subscribeEventCalled = false;
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(subscribeEventCalled, "Subscribe event not called.");
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await Task.Delay(250);
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
|
||||
var unsubscribeEventCalled = false;
|
||||
server.ClientUnsubscribedTopicAsync += e =>
|
||||
{
|
||||
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.UnsubscribeAsync("a");
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(unsubscribeEventCalled, "Unsubscribe event not called.");
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
}
|
||||
Assert.AreEqual(0, receivedMessages.Count, message: "The filter should not work!");
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Deny_Invalid_Topic()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
if (e.TopicFilter.Topic == "not_allowed_topic")
|
||||
{
|
||||
e.Response.ReasonCode = MqttSubscribeReasonCode.TopicFilterInvalid;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscribeResult = await client.SubscribeAsync("allowed_topic");
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.GrantedQoS0, subscribeResult.Items.First().ResultCode);
|
||||
|
||||
subscribeResult = await client.SubscribeAsync("not_allowed_topic");
|
||||
Assert.AreEqual(MqttClientSubscribeResultCode.TopicFilterInvalid, subscribeResult.Items.First().ResultCode);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Subscribe_With_User_Properties()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
InterceptingSubscriptionEventArgs eventArgs = null;
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
|
||||
await client.SubscribeAsync(subscribeOptions);
|
||||
|
||||
CollectionAssert.AreEqual(subscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Subscribing()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
// The client will be disconnected directly after subscribing!
|
||||
server.ClientSubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId);
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.SubscribeAsync("#");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Enqueue_Message_After_Subscription()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ClientSubscribedTopicAsync += _ =>
|
||||
{
|
||||
server.InjectApplicationMessage(new InjectedMqttApplicationMessage(new MqttApplicationMessageBuilder().WithTopic("test_topic").Build()));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(client);
|
||||
|
||||
await client.SubscribeAsync("test_topic");
|
||||
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Subscription()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.InterceptingSubscriptionAsync += e =>
|
||||
{
|
||||
// Set the topic to "a" regards what the client wants to subscribe.
|
||||
e.TopicFilter.Topic = "a";
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var topicAReceived = false;
|
||||
var topicBReceived = false;
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
if (e.ApplicationMessage.Topic == "a")
|
||||
{
|
||||
topicAReceived = true;
|
||||
}
|
||||
else if (e.ApplicationMessage.Topic == "b")
|
||||
{
|
||||
topicBReceived = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.SubscribeAsync("b");
|
||||
|
||||
await client.PublishStringAsync("a");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsTrue(topicAReceived);
|
||||
Assert.IsFalse(topicBReceived);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Response_Contains_Equal_Reason_Codes()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var subscribeOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a")
|
||||
.WithTopicFilter("b", MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
.WithTopicFilter("c", MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
.WithTopicFilter("d")
|
||||
.Build();
|
||||
|
||||
var response = await client.SubscribeAsync(subscribeOptions);
|
||||
|
||||
Assert.AreEqual(subscribeOptions.TopicFilters.Count, response.Items.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Lots_In_Multiple_Requests()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
var so = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(i.ToString()).Build();
|
||||
|
||||
await c1.SubscribeAsync(so).ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
messageBuilder.WithTopic(i.ToString());
|
||||
|
||||
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
|
||||
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessagesCount == 500, 5000);
|
||||
|
||||
Assert.AreEqual(500, receivedMessagesCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Lots_In_Single_Request()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var optionsBuilder = new MqttClientSubscribeOptionsBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
optionsBuilder.WithTopicFilter(i.ToString());
|
||||
}
|
||||
|
||||
await c1.SubscribeAsync(optionsBuilder.Build()).ConfigureAwait(false);
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
var messageBuilder = new MqttApplicationMessageBuilder();
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
messageBuilder.WithTopic(i.ToString());
|
||||
|
||||
await c2.PublishAsync(messageBuilder.Build()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => receivedMessagesCount == 500, TimeSpan.FromSeconds(20));
|
||||
|
||||
Assert.AreEqual(500, receivedMessagesCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Multiple_In_Multiple_Request()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").Build());
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("b").Build());
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("c").Build());
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c2.PublishStringAsync("a");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 1);
|
||||
|
||||
await c2.PublishStringAsync("b");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 2);
|
||||
|
||||
await c2.PublishStringAsync("c");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Multiple_In_Single_Request()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient();
|
||||
c1.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttClientSubscribeOptionsBuilder().WithTopicFilter("a").WithTopicFilter("b").WithTopicFilter("c").Build());
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient();
|
||||
|
||||
await c2.PublishStringAsync("a");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 1);
|
||||
|
||||
await c2.PublishStringAsync("b");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 2);
|
||||
|
||||
await c2.PublishStringAsync("c");
|
||||
await Task.Delay(100);
|
||||
Assert.AreEqual(receivedMessagesCount, 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_Unsubscribe()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c1"));
|
||||
c1.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("c2"));
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce).Build();
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(0, receivedMessagesCount);
|
||||
|
||||
var subscribeEventCalled = false;
|
||||
server.ClientSubscribedTopicAsync += e =>
|
||||
{
|
||||
subscribeEventCalled = e.TopicFilter.Topic == "a" && e.ClientId == c1.Options.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync(new MqttTopicFilter { Topic = "a", QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce });
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(subscribeEventCalled, "Subscribe event not called.");
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await Task.Delay(250);
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
|
||||
var unsubscribeEventCalled = false;
|
||||
server.ClientUnsubscribedTopicAsync += e =>
|
||||
{
|
||||
unsubscribeEventCalled = e.TopicFilter == "a" && e.ClientId == c1.Options.ClientId;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.UnsubscribeAsync("a");
|
||||
await Task.Delay(250);
|
||||
Assert.IsTrue(unsubscribeEventCalled, "Unsubscribe event not called.");
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await Task.Delay(500);
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
@@ -6,87 +6,81 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Subscription_Identifier_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Subscription_Identifier_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Subscription_Identifiers_Supported()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Subscription_Identifiers_Supported()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsTrue(connectResult.SubscriptionIdentifiersAvailable);
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(connectResult.SubscriptionIdentifiersAvailable);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_With_Subscription_Identifier()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
[TestMethod]
|
||||
public async Task Subscribe_With_Subscription_Identifier()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").Build();
|
||||
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic").Build();
|
||||
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
|
||||
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
await LongTestDelay();
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456);
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_With_Multiple_Subscription_Identifiers()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
[TestMethod]
|
||||
public async Task Subscribe_With_Multiple_Subscription_Identifiers()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var applicationMessageHandler = testEnvironment.CreateApplicationMessageHandler(client1);
|
||||
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic/A").Build();
|
||||
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
var topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic/A").Build();
|
||||
var subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(456).WithTopicFilter(topicFilter).Build();
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic/+").Build();
|
||||
subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(789).WithTopicFilter(topicFilter).Build();
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
topicFilter = testEnvironment.ClientFactory.CreateTopicFilterBuilder().WithTopic("Topic/+").Build();
|
||||
subscribeOptions = testEnvironment.ClientFactory.CreateSubscribeOptionsBuilder().WithSubscriptionIdentifier(789).WithTopicFilter(topicFilter).Build();
|
||||
await client1.SubscribeAsync(subscribeOptions);
|
||||
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
applicationMessageHandler.AssertReceivedCountEquals(0);
|
||||
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic/A", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
// The client will publish a message where it is itself subscribing to.
|
||||
await client1.PublishStringAsync("Topic/A", "Payload", retain: true);
|
||||
await LongTestDelay();
|
||||
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
applicationMessageHandler.AssertReceivedCountEquals(1);
|
||||
|
||||
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456);
|
||||
applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(789);
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(456));
|
||||
Assert.IsTrue(applicationMessageHandler.ReceivedEventArgs[0].ApplicationMessage.SubscriptionIdentifiers.Contains(789));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,188 +12,184 @@ using MQTTnet.Formatter;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Tls_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Tls_Tests : BaseTestClass
|
||||
static X509Certificate2 CreateCertificate(string oid)
|
||||
{
|
||||
static X509Certificate2 CreateCertificate(string oid)
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||
sanBuilder.AddDnsName("localhost");
|
||||
|
||||
using var rsa = RSA.Create();
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
using var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10));
|
||||
var pfxCertificate = new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx),
|
||||
(string)null,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||
|
||||
return pfxCertificate;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Tls_Swap_Test()
|
||||
{
|
||||
var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var serverOptionsBuilder = testEnvironment.ServerFactory.CreateServerOptionsBuilder();
|
||||
|
||||
var firstOid = "1.3.6.1.5.5.7.3.1";
|
||||
var secondOid = "1.3.6.1.5.5.7.3.2";
|
||||
|
||||
var certificateProvider = new CertificateProvider
|
||||
{
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||
sanBuilder.AddDnsName("localhost");
|
||||
CurrentCertificate = CreateCertificate(firstOid)
|
||||
};
|
||||
|
||||
using (var rsa = RSA.Create())
|
||||
{
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
serverOptionsBuilder.WithoutDefaultEndpoint().WithEncryptedEndpoint().WithEncryptionSslProtocol(SslProtocols.Tls12).WithEncryptionCertificate(certificateProvider);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
var serverOptions = serverOptionsBuilder.Build();
|
||||
|
||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
|
||||
var server = testEnvironment.CreateServer(serverOptions);
|
||||
|
||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
||||
{
|
||||
var pfxCertificate = new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx),
|
||||
(string)null,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||
|
||||
return pfxCertificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Tls_Swap_Test()
|
||||
var publishedCount = 0;
|
||||
server.InterceptingPublishAsync += _ =>
|
||||
{
|
||||
var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var serverOptionsBuilder = testEnvironment.ServerFactory.CreateServerOptionsBuilder();
|
||||
Interlocked.Increment(ref publishedCount);
|
||||
|
||||
var firstOid = "1.3.6.1.5.5.7.3.1";
|
||||
var secondOid = "1.3.6.1.5.5.7.3.2";
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
var certificateProvider = new CertificateProvider
|
||||
await server.StartAsync();
|
||||
|
||||
var firstClient = await ConnectClientAsync(
|
||||
testEnvironment,
|
||||
args =>
|
||||
{
|
||||
CurrentCertificate = CreateCertificate(firstOid)
|
||||
};
|
||||
Assert.AreEqual(firstOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
|
||||
return true;
|
||||
});
|
||||
|
||||
serverOptionsBuilder.WithoutDefaultEndpoint().WithEncryptedEndpoint().WithEncryptionSslProtocol(SslProtocols.Tls12).WithEncryptionCertificate(certificateProvider);
|
||||
var firstClientReceivedCount = 0;
|
||||
firstClient.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
Interlocked.Increment(ref firstClientReceivedCount);
|
||||
|
||||
var serverOptions = serverOptionsBuilder.Build();
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
var server = testEnvironment.CreateServer(serverOptions);
|
||||
await firstClient.SubscribeAsync("TestTopic1");
|
||||
|
||||
var publishedCount = 0;
|
||||
server.InterceptingPublishAsync += args =>
|
||||
await firstClient.PublishAsync(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Interlocked.Increment(ref publishedCount);
|
||||
Topic = "TestTopic1",
|
||||
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StartAsync();
|
||||
|
||||
var firstClient = await ConnectClientAsync(
|
||||
testEnvironment,
|
||||
args =>
|
||||
{
|
||||
Assert.AreEqual(firstOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
|
||||
return true;
|
||||
});
|
||||
|
||||
var firstClientReceivedCount = 0;
|
||||
firstClient.ApplicationMessageReceivedAsync += args =>
|
||||
{
|
||||
Interlocked.Increment(ref firstClientReceivedCount);
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await firstClient.SubscribeAsync("TestTopic1");
|
||||
|
||||
await firstClient.PublishAsync(
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
|
||||
});
|
||||
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
|
||||
}));
|
||||
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
|
||||
}));
|
||||
certificateProvider.CurrentCertificate = CreateCertificate(secondOid);
|
||||
|
||||
certificateProvider.CurrentCertificate = CreateCertificate(secondOid);
|
||||
|
||||
// Validate that the certificate was switched
|
||||
var secondClient = await ConnectClientAsync(
|
||||
testEnvironment,
|
||||
args =>
|
||||
{
|
||||
Assert.AreEqual(secondOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
|
||||
return true;
|
||||
});
|
||||
|
||||
var secondClientReceivedCount = 0;
|
||||
secondClient.ApplicationMessageReceivedAsync += args =>
|
||||
// Validate that the certificate was switched
|
||||
var secondClient = await ConnectClientAsync(
|
||||
testEnvironment,
|
||||
args =>
|
||||
{
|
||||
Interlocked.Increment(ref secondClientReceivedCount);
|
||||
Assert.AreEqual(secondOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
|
||||
return true;
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
var secondClientReceivedCount = 0;
|
||||
secondClient.ApplicationMessageReceivedAsync += _ =>
|
||||
{
|
||||
Interlocked.Increment(ref secondClientReceivedCount);
|
||||
|
||||
await secondClient.SubscribeAsync("TestTopic2");
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await firstClient.PublishAsync(
|
||||
await secondClient.SubscribeAsync("TestTopic2");
|
||||
|
||||
await firstClient.PublishAsync(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic2",
|
||||
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
|
||||
});
|
||||
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic2",
|
||||
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
|
||||
});
|
||||
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
|
||||
}));
|
||||
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic2",
|
||||
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
|
||||
}));
|
||||
// Ensure first client still works
|
||||
await firstClient.PublishAsync(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
|
||||
});
|
||||
|
||||
// Ensure first client still works
|
||||
await firstClient.PublishAsync(
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
|
||||
PayloadSegment = new ArraySegment<byte>([1, 2, 3, 4])
|
||||
}));
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.AreEqual(6, publishedCount);
|
||||
Assert.AreEqual(4, firstClientReceivedCount);
|
||||
Assert.AreEqual(2, secondClientReceivedCount);
|
||||
|
||||
await server.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
static async Task<IMqttClient> ConnectClientAsync(TestEnvironment testEnvironment, Func<MqttClientCertificateValidationEventArgs, bool> certValidator)
|
||||
{
|
||||
var clientOptionsBuilder = testEnvironment.ClientFactory.CreateClientOptionsBuilder();
|
||||
clientOptionsBuilder.WithClientId(Guid.NewGuid().ToString())
|
||||
.WithTcpServer("localhost", 8883)
|
||||
.WithTlsOptions(
|
||||
o =>
|
||||
{
|
||||
o.WithSslProtocols(SslProtocols.Tls12).WithCertificateValidationHandler(certValidator);
|
||||
});
|
||||
|
||||
await testEnvironment.Server.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(
|
||||
new MqttApplicationMessage
|
||||
{
|
||||
Topic = "TestTopic1",
|
||||
PayloadSegment = new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 })
|
||||
}));
|
||||
var clientOptions = clientOptionsBuilder.Build();
|
||||
return await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Task.Delay(1000);
|
||||
sealed class CertificateProvider : ICertificateProvider
|
||||
{
|
||||
public X509Certificate2 CurrentCertificate { get; set; }
|
||||
|
||||
Assert.AreEqual(6, publishedCount);
|
||||
Assert.AreEqual(4, firstClientReceivedCount);
|
||||
Assert.AreEqual(2, secondClientReceivedCount);
|
||||
|
||||
await server.StopAsync();
|
||||
}
|
||||
|
||||
static async Task<IMqttClient> ConnectClientAsync(TestEnvironment testEnvironment, Func<MqttClientCertificateValidationEventArgs, bool> certValidator)
|
||||
public X509Certificate2 GetCertificate()
|
||||
{
|
||||
var clientOptionsBuilder = testEnvironment.ClientFactory.CreateClientOptionsBuilder();
|
||||
clientOptionsBuilder.WithClientId(Guid.NewGuid().ToString())
|
||||
.WithTcpServer("localhost", 8883)
|
||||
.WithTlsOptions(
|
||||
o =>
|
||||
{
|
||||
o.WithSslProtocols(SslProtocols.Tls12).WithCertificateValidationHandler(certValidator);
|
||||
});
|
||||
|
||||
var clientOptions = clientOptionsBuilder.Build();
|
||||
return await testEnvironment.ConnectClient(clientOptions);
|
||||
}
|
||||
|
||||
sealed class CertificateProvider : ICertificateProvider
|
||||
{
|
||||
public X509Certificate2 CurrentCertificate { get; set; }
|
||||
|
||||
public X509Certificate2 GetCertificate()
|
||||
{
|
||||
return CurrentCertificate;
|
||||
}
|
||||
return CurrentCertificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,72 +9,68 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Topic_Alias_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Topic_Alias_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Topic_Alias_Supported()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Topic_Alias_Supported()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build());
|
||||
var connectResult = await client.ConnectAsync(new MqttClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.Build());
|
||||
|
||||
Assert.AreEqual(connectResult.TopicAliasMaximum, ushort.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_With_Topic_Alias()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receivedTopics = new List<string>();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
lock (receivedTopics)
|
||||
{
|
||||
receivedTopics.Add(e.ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync("#");
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var message = new MqttApplicationMessage
|
||||
{
|
||||
Topic = "this_is_the_topic",
|
||||
TopicAlias = 22
|
||||
};
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
message.Topic = null;
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(3, receivedTopics.Count);
|
||||
CollectionAssert.AllItemsAreNotNull(receivedTopics);
|
||||
Assert.IsTrue(receivedTopics.All(t => t.Equals("this_is_the_topic")));
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(connectResult.TopicAliasMaximum, ushort.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_With_Topic_Alias()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receivedTopics = new List<string>();
|
||||
|
||||
var c1 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
c1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
lock (receivedTopics)
|
||||
{
|
||||
receivedTopics.Add(e.ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await c1.SubscribeAsync("#");
|
||||
|
||||
var c2 = await testEnvironment.ConnectClient(options => options.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var message = new MqttApplicationMessage
|
||||
{
|
||||
Topic = "this_is_the_topic",
|
||||
TopicAlias = 22
|
||||
};
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
message.Topic = null;
|
||||
|
||||
await c2.PublishAsync(message);
|
||||
await c2.PublishAsync(message);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(3, receivedTopics.Count);
|
||||
CollectionAssert.AllItemsAreNotNull(receivedTopics);
|
||||
Assert.IsTrue(receivedTopics.All(t => t.Equals("this_is_the_topic")));
|
||||
}
|
||||
}
|
||||
@@ -8,52 +8,47 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Unsubscribe_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Unsubscribe_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Unsubscribing()
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttClientDisconnectedException))]
|
||||
public async Task Disconnect_While_Unsubscribing()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
// The client will be disconnected directly after subscribing!
|
||||
server.ClientUnsubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId);
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.SubscribeAsync("#");
|
||||
await client.UnsubscribeAsync("#");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Unsubscribe_With_User_Properties()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
InterceptingUnsubscriptionEventArgs eventArgs = null;
|
||||
server.InterceptingUnsubscriptionAsync += e =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
// The client will be disconnect directly after subscribing!
|
||||
server.ClientUnsubscribedTopicAsync += ev => server.DisconnectClientAsync(ev.ClientId, MqttDisconnectReasonCode.NormalDisconnection);
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
await client.SubscribeAsync("#");
|
||||
await client.UnsubscribeAsync("#");
|
||||
}
|
||||
}
|
||||
var unsubscribeOptions = testEnvironment.ClientFactory.CreateUnsubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
|
||||
await client.UnsubscribeAsync(unsubscribeOptions);
|
||||
|
||||
[TestMethod]
|
||||
public async Task Intercept_Unsubscribe_With_User_Properties()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
InterceptingUnsubscriptionEventArgs eventArgs = null;
|
||||
server.InterceptingUnsubscriptionAsync += e =>
|
||||
{
|
||||
eventArgs = e;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var unsubscribeOptions = testEnvironment.ClientFactory.CreateUnsubscribeOptionsBuilder().WithTopicFilter("X").WithUserProperty("A", "1").Build();
|
||||
await client.UnsubscribeAsync(unsubscribeOptions);
|
||||
|
||||
CollectionAssert.AreEqual(unsubscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
|
||||
}
|
||||
}
|
||||
CollectionAssert.AreEqual(unsubscribeOptions.UserProperties.ToList(), eventArgs.UserProperties);
|
||||
}
|
||||
}
|
||||
@@ -2,64 +2,56 @@
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public class Feature_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public class Feature_Tests
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_User_Properties()
|
||||
{
|
||||
public TestContext TestContext { get; set; }
|
||||
using var testEnvironment = new TestEnvironment(TestContext);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_User_Properties()
|
||||
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("A")
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithUserProperty("z", "3")
|
||||
.WithUserProperty("z", "4"); // z is here two times to test list of items
|
||||
|
||||
await receiver.SubscribeAsync(new MqttClientSubscribeOptions
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
TopicFilters = [new MqttTopicFilter { Topic = "#" }]
|
||||
}, CancellationToken.None);
|
||||
|
||||
var sender = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
receiver.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var message = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("A")
|
||||
.WithUserProperty("x", "1")
|
||||
.WithUserProperty("y", "2")
|
||||
.WithUserProperty("z", "3")
|
||||
.WithUserProperty("z", "4"); // z is here two times to test list of items
|
||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||
|
||||
await receiver.SubscribeAsync(new MqttClientSubscribeOptions
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter>
|
||||
{
|
||||
new MqttTopicFilter { Topic = "#" }
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
await Task.Delay(1000);
|
||||
|
||||
MqttApplicationMessage receivedMessage = null;
|
||||
receiver.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessage = e.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await sender.PublishAsync(message.Build(), CancellationToken.None);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.IsNotNull(receivedMessage);
|
||||
Assert.AreEqual("A", receivedMessage.Topic);
|
||||
Assert.AreEqual(4, receivedMessage.UserProperties.Count);
|
||||
|
||||
}
|
||||
}
|
||||
Assert.IsNotNull(receivedMessage);
|
||||
Assert.AreEqual("A", receivedMessage.Topic);
|
||||
Assert.AreEqual(4, receivedMessage.UserProperties.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,41 +6,37 @@ using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Wildcard_Subscription_Available_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Wildcard_Subscription_Available_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V3()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V3()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V311)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V5()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
[TestMethod]
|
||||
public async Task Server_Reports_Wildcard_Subscription_Available_Tests_Supported_V5()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
var client = testEnvironment.CreateClient();
|
||||
var connectResult = await client.ConnectAsync(testEnvironment.ClientFactory.CreateClientOptionsBuilder()
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
|
||||
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(connectResult.WildcardSubscriptionAvailable);
|
||||
}
|
||||
}
|
||||
@@ -3,78 +3,72 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Server
|
||||
namespace MQTTnet.Tests.Server;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class Will_Tests : BaseTestClass
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class Will_Tests : BaseTestClass
|
||||
[TestMethod]
|
||||
public async Task Intercept_Will_Message()
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Intercept_Will_Message()
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
var server = await testEnvironment.StartServer().ConfigureAwait(false);
|
||||
|
||||
MqttApplicationMessage willMessage = null;
|
||||
server.InterceptingPublishAsync += eventArgs =>
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer().ConfigureAwait(false);
|
||||
willMessage = eventArgs.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
MqttApplicationMessage willMessage = null;
|
||||
server.InterceptingPublishAsync += eventArgs =>
|
||||
{
|
||||
willMessage = eventArgs.ApplicationMessage;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
await testEnvironment.ConnectClient(new MqttClientOptionsBuilder()).ConfigureAwait(false);
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
|
||||
var takeOverClient = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
|
||||
takeOverClient.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
|
||||
|
||||
await testEnvironment.ConnectClient(new MqttClientOptionsBuilder()).ConfigureAwait(false);
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
|
||||
var takeOverClient = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
|
||||
takeOverClient.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
|
||||
await LongTestDelay().ConfigureAwait(false);
|
||||
|
||||
await LongTestDelay().ConfigureAwait(false);
|
||||
Assert.IsNotNull(willMessage);
|
||||
}
|
||||
|
||||
Assert.IsNotNull(willMessage);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Will_Message_Do_Not_Send_On_Clean_Disconnect()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Will_Message_Do_Not_Send_On_Clean_Disconnect()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var receiver = await testEnvironment.ConnectClient().ConfigureAwait(false);
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient().ConfigureAwait(false);
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
await receiver.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
|
||||
await receiver.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will");
|
||||
var sender = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
|
||||
await sender.DisconnectAsync().ConfigureAwait(false);
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will");
|
||||
var sender = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
|
||||
await sender.DisconnectAsync().ConfigureAwait(false);
|
||||
await LongTestDelay().ConfigureAwait(false);
|
||||
|
||||
await LongTestDelay().ConfigureAwait(false);
|
||||
Assert.AreEqual(0, receivedMessages.ReceivedEventArgs.Count);
|
||||
}
|
||||
|
||||
Assert.AreEqual(0, receivedMessages.ReceivedEventArgs.Count);
|
||||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task Will_Message_Send()
|
||||
{
|
||||
using var testEnvironment = CreateTestEnvironment();
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
[TestMethod]
|
||||
public async Task Will_Message_Send()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder()).ConfigureAwait(false);
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
await receiver.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
|
||||
var receiver = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder()).ConfigureAwait(false);
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(receiver);
|
||||
await receiver.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("#").Build());
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
|
||||
var takeOverClient = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
|
||||
takeOverClient.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithWillTopic("My/last/will").WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce);
|
||||
var takeOverClient = await testEnvironment.ConnectClient(clientOptions).ConfigureAwait(false);
|
||||
takeOverClient.Dispose(); // Dispose will not send a DISCONNECT pattern first so the will message must be sent.
|
||||
await LongTestDelay().ConfigureAwait(false);
|
||||
|
||||
await LongTestDelay().ConfigureAwait(false);
|
||||
|
||||
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
|
||||
}
|
||||
}
|
||||
Assert.AreEqual(1, receivedMessages.ReceivedEventArgs.Count);
|
||||
}
|
||||
}
|
||||
@@ -3,162 +3,161 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Server.Internal;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
[TestClass]
|
||||
public sealed class MqttTopicFilterComparer_Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttTopicFilterComparer_Tests
|
||||
[TestMethod]
|
||||
public void AllLevelsWildcardMatch()
|
||||
{
|
||||
[TestMethod]
|
||||
public void AllLevelsWildcardMatch()
|
||||
CompareAndAssert("A/B/C/D", "#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BeginningOneLevelWildcardMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "+/B/C", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Compare_UTF8_String_Match()
|
||||
{
|
||||
CompareAndAssert("öäüß", "öäüß", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Compare_UTF8_String_No_Match()
|
||||
{
|
||||
CompareAndAssert("ae", "ä", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DirectMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "A/B/C", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DirectNoMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/X", "A/B/C", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndMultipleLevelsWildcardMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "A/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndMultipleLevelsWildcardMatchEmptyLevel()
|
||||
{
|
||||
CompareAndAssert("A/", "A/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndMultipleLevelsWildcardNoMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C/D", "A/C/#", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndOneLevelWildcardMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "A/B/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Hash_Match_With_Separator_Only()
|
||||
{
|
||||
//A Topic Name or Topic Filter consisting only of the ‘/’ character is valid
|
||||
CompareAndAssert("/", "#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MiddleOneLevelWildcardMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "A/+/C", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MiddleOneLevelWildcardNoMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C/D", "A/+/C", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MultiLevel_Sport()
|
||||
{
|
||||
// Tests from official MQTT spec (4.7.1.2 Multi-level wildcard)
|
||||
CompareAndAssert("sport/tennis/player1", "sport/tennis/player1/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport/tennis/player1/ranking", "sport/tennis/player1/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport/tennis/player1/score/wimbledon", "sport/tennis/player1/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
|
||||
CompareAndAssert("sport/tennis/player1", "sport/tennis/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport/tennis/player2", "sport/tennis/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport/tennis/player1/ranking", "sport/tennis/+", MqttTopicFilterCompareResult.NoMatch);
|
||||
|
||||
CompareAndAssert("sport", "sport/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport", "sport/+", MqttTopicFilterCompareResult.NoMatch);
|
||||
CompareAndAssert("sport/", "sport/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Plus_Match_With_Separator_Only()
|
||||
{
|
||||
//A Topic Name or Topic Filter consisting only of the ‘/’ character is valid
|
||||
CompareAndAssert("A", "+", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reserved_Multi_Level_Wildcard_Only()
|
||||
{
|
||||
CompareAndAssert("$SPECIAL/TOPIC", "#", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reserved_Single_Level_Wildcard()
|
||||
{
|
||||
CompareAndAssert("$SYS/monitor/Clients", "$SYS/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reserved_Single_Level_Wildcard_Prefix()
|
||||
{
|
||||
CompareAndAssert("$SYS/monitor/Clients", "+/monitor/Clients", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reserved_Single_Level_Wildcard_Suffix()
|
||||
{
|
||||
CompareAndAssert("$SYS/monitor/Clients", "$SYS/monitor/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SingleLevel_Finance()
|
||||
{
|
||||
// Tests from official MQTT spec (4.7.1.3 Single level wildcard)
|
||||
CompareAndAssert("/finance", "+/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("/finance", "/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("/finance", "+", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
static void CompareAndAssert(string topic, string filter, MqttTopicFilterCompareResult expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, MqttTopicFilterComparer.Compare(topic, filter));
|
||||
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
|
||||
MqttTopicHash.Calculate(filter, out var filterTopicHash, out var filterTopicHashMask, out _);
|
||||
|
||||
if (expectedResult == MqttTopicFilterCompareResult.IsMatch)
|
||||
{
|
||||
CompareAndAssert("A/B/C/D", "#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BeginningOneLevelWildcardMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "+/B/C", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Compare_UTF8_String_Match()
|
||||
{
|
||||
CompareAndAssert("öäüß", "öäüß", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Compare_UTF8_String_No_Match()
|
||||
{
|
||||
CompareAndAssert("ae", "ä", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DirectMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "A/B/C", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DirectNoMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/X", "A/B/C", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndMultipleLevelsWildcardMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "A/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndMultipleLevelsWildcardMatchEmptyLevel()
|
||||
{
|
||||
CompareAndAssert("A/", "A/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndMultipleLevelsWildcardNoMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C/D", "A/C/#", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndOneLevelWildcardMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "A/B/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Hash_Match_With_Separator_Only()
|
||||
{
|
||||
//A Topic Name or Topic Filter consisting only of the ‘/’ character is valid
|
||||
CompareAndAssert("/", "#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MiddleOneLevelWildcardMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C", "A/+/C", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MiddleOneLevelWildcardNoMatch()
|
||||
{
|
||||
CompareAndAssert("A/B/C/D", "A/+/C", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MultiLevel_Sport()
|
||||
{
|
||||
// Tests from official MQTT spec (4.7.1.2 Multi-level wildcard)
|
||||
CompareAndAssert("sport/tennis/player1", "sport/tennis/player1/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport/tennis/player1/ranking", "sport/tennis/player1/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport/tennis/player1/score/wimbledon", "sport/tennis/player1/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
|
||||
CompareAndAssert("sport/tennis/player1", "sport/tennis/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport/tennis/player2", "sport/tennis/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport/tennis/player1/ranking", "sport/tennis/+", MqttTopicFilterCompareResult.NoMatch);
|
||||
|
||||
CompareAndAssert("sport", "sport/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("sport", "sport/+", MqttTopicFilterCompareResult.NoMatch);
|
||||
CompareAndAssert("sport/", "sport/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Plus_Match_With_Separator_Only()
|
||||
{
|
||||
//A Topic Name or Topic Filter consisting only of the ‘/’ character is valid
|
||||
CompareAndAssert("A", "+", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reserved_Multi_Level_Wildcard_Only()
|
||||
{
|
||||
CompareAndAssert("$SPECIAL/TOPIC", "#", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reserved_Single_Level_Wildcard()
|
||||
{
|
||||
CompareAndAssert("$SYS/monitor/Clients", "$SYS/#", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reserved_Single_Level_Wildcard_Prefix()
|
||||
{
|
||||
CompareAndAssert("$SYS/monitor/Clients", "+/monitor/Clients", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reserved_Single_Level_Wildcard_Suffix()
|
||||
{
|
||||
CompareAndAssert("$SYS/monitor/Clients", "$SYS/monitor/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SingleLevel_Finance()
|
||||
{
|
||||
// Tests from official MQTT spec (4.7.1.3 Single level wildcard)
|
||||
CompareAndAssert("/finance", "+/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("/finance", "/+", MqttTopicFilterCompareResult.IsMatch);
|
||||
CompareAndAssert("/finance", "+", MqttTopicFilterCompareResult.NoMatch);
|
||||
}
|
||||
|
||||
static void CompareAndAssert(string topic, string filter, MqttTopicFilterCompareResult expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, MqttTopicFilterComparer.Compare(topic, filter));
|
||||
|
||||
MqttTopicHash.Calculate(topic, out var topicHash, out _, out _);
|
||||
MqttTopicHash.Calculate(filter, out var filterTopicHash, out var filterTopicHashMask, out _);
|
||||
|
||||
if (expectedResult == MqttTopicFilterCompareResult.IsMatch)
|
||||
{
|
||||
// If it matches then the hash evaluation should also indicate a match
|
||||
Assert.IsTrue((topicHash & filterTopicHashMask) == filterTopicHash, "Incorrect topic hash (is equal)");
|
||||
}
|
||||
// If it matches then the hash evaluation should also indicate a match
|
||||
Assert.IsTrue((topicHash & filterTopicHashMask) == filterTopicHash, "Incorrect topic hash (is equal)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,92 +2,91 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Extensions.TopicTemplate;
|
||||
|
||||
namespace MQTTnet.Tests
|
||||
namespace MQTTnet.Tests;
|
||||
|
||||
public class TopicGenerator
|
||||
{
|
||||
public class TopicGenerator
|
||||
public static void Generate(
|
||||
int numPublishers, int numTopicsPerPublisher,
|
||||
out Dictionary<string, List<string>> topicsByPublisher,
|
||||
out Dictionary<string, List<string>> singleWildcardTopicsByPublisher,
|
||||
out Dictionary<string, List<string>> multiWildcardTopicsByPublisher
|
||||
)
|
||||
{
|
||||
public static void Generate(
|
||||
int numPublishers, int numTopicsPerPublisher,
|
||||
out Dictionary<string, List<string>> topicsByPublisher,
|
||||
out Dictionary<string, List<string>> singleWildcardTopicsByPublisher,
|
||||
out Dictionary<string, List<string>> multiWildcardTopicsByPublisher
|
||||
)
|
||||
topicsByPublisher = new Dictionary<string, List<string>>();
|
||||
singleWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||
multiWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||
|
||||
// Find some reasonable distribution across three topic levels
|
||||
var topicsPerLevel = (int)Math.Pow(numTopicsPerPublisher, (1.0 / 3.0));
|
||||
if (topicsPerLevel <= 0)
|
||||
{
|
||||
topicsByPublisher = new Dictionary<string, List<string>>();
|
||||
singleWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||
multiWildcardTopicsByPublisher = new Dictionary<string, List<string>>();
|
||||
topicsPerLevel = 1;
|
||||
}
|
||||
|
||||
// Find some reasonable distribution across three topic levels
|
||||
var topicsPerLevel = (int)Math.Pow(numTopicsPerPublisher, (1.0 / 3.0));
|
||||
if (topicsPerLevel <= 0)
|
||||
int numLevel1Topics = topicsPerLevel;
|
||||
int numLevel2Topics = topicsPerLevel;
|
||||
|
||||
var maxNumLevel3Topics = 1 + (int)((double)numTopicsPerPublisher / numLevel1Topics / numLevel2Topics);
|
||||
if (maxNumLevel3Topics <= 0)
|
||||
{
|
||||
maxNumLevel3Topics = 1;
|
||||
}
|
||||
|
||||
MqttTopicTemplate baseTemplate = new MqttTopicTemplate("{publisher}/{building}/{level}/{sensor}");
|
||||
|
||||
for (var p = 0; p < numPublishers; ++p)
|
||||
{
|
||||
int publisherTopicCount = 0;
|
||||
var publisherName = "pub" + p;
|
||||
var publisherTemplate = baseTemplate
|
||||
.WithParameter("publisher", publisherName);
|
||||
|
||||
for (var l1 = 0; l1 < numLevel1Topics; ++l1)
|
||||
{
|
||||
topicsPerLevel = 1;
|
||||
}
|
||||
|
||||
int numLevel1Topics = topicsPerLevel;
|
||||
int numLevel2Topics = topicsPerLevel;
|
||||
|
||||
var maxNumLevel3Topics = 1 + (int)((double)numTopicsPerPublisher / numLevel1Topics / numLevel2Topics);
|
||||
if (maxNumLevel3Topics <= 0)
|
||||
{
|
||||
maxNumLevel3Topics = 1;
|
||||
}
|
||||
|
||||
MqttTopicTemplate baseTemplate = new MqttTopicTemplate("{publisher}/{building}/{level}/{sensor}");
|
||||
|
||||
for (var p = 0; p < numPublishers; ++p)
|
||||
{
|
||||
int publisherTopicCount = 0;
|
||||
var publisherName = "pub" + p;
|
||||
var publisherTemplate = baseTemplate
|
||||
.WithParameter("publisher", publisherName);
|
||||
|
||||
for (var l1 = 0; l1 < numLevel1Topics; ++l1)
|
||||
var l1Template = publisherTemplate.WithParameter("building", "building" + (l1 + 1));
|
||||
for (var l2 = 0; l2 < numLevel2Topics; ++l2)
|
||||
{
|
||||
var l1Template = publisherTemplate.WithParameter("building", "building" + (l1 + 1));
|
||||
for (var l2 = 0; l2 < numLevel2Topics; ++l2)
|
||||
var l2Template = l1Template.WithParameter("level", "level" + (l2 + 1));
|
||||
for (var l3 = 0; l3 < maxNumLevel3Topics; ++l3)
|
||||
{
|
||||
var l2Template = l1Template.WithParameter("level", "level" + (l2 + 1));
|
||||
for (var l3 = 0; l3 < maxNumLevel3Topics; ++l3)
|
||||
if (publisherTopicCount >= numTopicsPerPublisher)
|
||||
break;
|
||||
|
||||
var l3Template = l2Template
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1));
|
||||
AddPublisherTopic(publisherName, l3Template.TopicFilter, topicsByPublisher);
|
||||
|
||||
if (l2 == 0)
|
||||
{
|
||||
if (publisherTopicCount >= numTopicsPerPublisher)
|
||||
break;
|
||||
|
||||
var l3Template = l2Template
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1));
|
||||
AddPublisherTopic(publisherName, l3Template.TopicFilter, topicsByPublisher);
|
||||
|
||||
if (l2 == 0)
|
||||
{
|
||||
var singleWildcardTopic = l1Template
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, singleWildcardTopic, singleWildcardTopicsByPublisher);
|
||||
}
|
||||
if ((l1 == 0) && (l3 == 0))
|
||||
{
|
||||
var multiWildcardTopic = publisherTemplate
|
||||
.WithParameter("level", "level" + (l2 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, multiWildcardTopic, multiWildcardTopicsByPublisher);
|
||||
}
|
||||
|
||||
++publisherTopicCount;
|
||||
var singleWildcardTopic = l1Template
|
||||
.WithParameter("sensor", "sensor" + (l3 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, singleWildcardTopic, singleWildcardTopicsByPublisher);
|
||||
}
|
||||
if ((l1 == 0) && (l3 == 0))
|
||||
{
|
||||
var multiWildcardTopic = publisherTemplate
|
||||
.WithParameter("level", "level" + (l2 + 1))
|
||||
.TopicFilter;
|
||||
AddPublisherTopic(publisherName, multiWildcardTopic, multiWildcardTopicsByPublisher);
|
||||
}
|
||||
|
||||
++publisherTopicCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AddPublisherTopic(string publisherName, string topic, Dictionary<string, List<string>> topicsByPublisher)
|
||||
{
|
||||
List<string> topicList;
|
||||
if (!topicsByPublisher.TryGetValue(publisherName, out topicList))
|
||||
{
|
||||
topicList = new List<string>();
|
||||
topicsByPublisher.Add(publisherName, topicList);
|
||||
}
|
||||
topicList.Add(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AddPublisherTopic(string publisherName, string topic, Dictionary<string, List<string>> topicsByPublisher)
|
||||
{
|
||||
List<string> topicList;
|
||||
if (!topicsByPublisher.TryGetValue(publisherName, out topicList))
|
||||
{
|
||||
topicList = new List<string>();
|
||||
topicsByPublisher.Add(publisherName, topicList);
|
||||
}
|
||||
topicList.Add(topic);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user