mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-19 02:53:06 +08:00
More options for disconnect (#1557)
* Expose more options in disconnect options. * Expose more options in disconnect API. * Refactor code. * Expose user properties in disconnected event. * Expose more disconnect options in server. * Fix build errors * Add new samples. * Fix client cleanup * Fix merge conflicts
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// 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;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.LowLevelClient;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Tests.Clients.LowLevelMqttClient
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class LowLevelMqttClient_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Authenticate()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var factory = new MqttFactory();
|
||||
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.Factory.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 MqttFactory().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 MqttFactory().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
|
||||
{
|
||||
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.");
|
||||
}
|
||||
|
||||
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 timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
||||
{
|
||||
await lowLevelClient.SendAsync(MqttPingReqPacket.Instance, CancellationToken.None);
|
||||
await LongTestDelay();
|
||||
|
||||
await lowLevelClient.ReceiveAsync(timeout.Token);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
Assert.IsFalse(lowLevelClient.IsConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var factory = new MqttFactory();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,544 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Extensions.ManagedClient;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Tests.Clients.ManagedMqttClient
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class ManagedMqttClient_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task Receive_While_Not_Cleanly_Disconnected()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
await testEnvironment.StartServer(o => o.WithPersistentSessions());
|
||||
|
||||
var senderClient = await testEnvironment.ConnectClient();
|
||||
|
||||
// Prepare managed client.
|
||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient();
|
||||
await managedClient.SubscribeAsync("#");
|
||||
var receivedMessages = testEnvironment.CreateApplicationMessageHandler(managedClient);
|
||||
|
||||
var managedClientOptions = new ManagedMqttClientOptions
|
||||
{
|
||||
ClientOptions = testEnvironment.Factory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithClientId(nameof(Receive_While_Not_Cleanly_Disconnected) + "_managed")
|
||||
.WithCleanSession(false)
|
||||
.Build()
|
||||
};
|
||||
|
||||
await managedClient.StartAsync(managedClientOptions);
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
// Send test data.
|
||||
await senderClient.PublishStringAsync("topic1");
|
||||
await LongTestDelay();
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(1);
|
||||
|
||||
// Stop the managed client but keep session at server (not clean disconnect required).
|
||||
await managedClient.StopAsync(false);
|
||||
await LongTestDelay();
|
||||
|
||||
// Send new messages in the meantime.
|
||||
await senderClient.PublishStringAsync("topic2", qualityOfServiceLevel: MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
await LongTestDelay();
|
||||
|
||||
// Start the managed client, it should receive the new message.
|
||||
await managedClient.StartAsync(managedClientOptions);
|
||||
await LongTestDelay();
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(2);
|
||||
|
||||
// Stop and start again, no new message should be received.
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
await managedClient.StopAsync(false);
|
||||
await LongTestDelay();
|
||||
await managedClient.StartAsync(managedClientOptions);
|
||||
await LongTestDelay();
|
||||
}
|
||||
|
||||
receivedMessages.AssertReceivedCountEquals(2);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_To_Invalid_Server()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var mqttClient = testEnvironment.CreateClient();
|
||||
|
||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(mqttClient);
|
||||
|
||||
ConnectingFailedEventArgs connectingFailedEventArgs = null;
|
||||
managedClient.ConnectingFailedAsync += args =>
|
||||
{
|
||||
connectingFailedEventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await managedClient.StartAsync(
|
||||
new ManagedMqttClientOptions
|
||||
{
|
||||
ClientOptions = testEnvironment.Factory.CreateClientOptionsBuilder().WithTimeout(TimeSpan.FromSeconds(2)).WithTcpServer("wrong_server", 1234).Build()
|
||||
});
|
||||
|
||||
await managedClient.EnqueueAsync("test_topic_2");
|
||||
|
||||
SpinWait.SpinUntil(() => connectingFailedEventArgs != null, 10000);
|
||||
|
||||
// The wrong server must be reported in general.
|
||||
Assert.IsNotNull(connectingFailedEventArgs);
|
||||
Assert.IsNotNull(connectingFailedEventArgs.Exception);
|
||||
Assert.IsNull(connectingFailedEventArgs.ConnectResult);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Drop_New_Messages_On_Full_Queue()
|
||||
{
|
||||
var factory = new MqttFactory();
|
||||
var managedClient = factory.CreateManagedMqttClient();
|
||||
try
|
||||
{
|
||||
var clientOptions = new ManagedMqttClientOptionsBuilder().WithMaxPendingMessages(5)
|
||||
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage);
|
||||
|
||||
clientOptions.WithClientOptions(o => o.WithTcpServer("localhost"));
|
||||
|
||||
await managedClient.StartAsync(clientOptions.Build());
|
||||
|
||||
await managedClient.EnqueueAsync("1");
|
||||
await managedClient.EnqueueAsync("2");
|
||||
await managedClient.EnqueueAsync("3");
|
||||
await managedClient.EnqueueAsync("4");
|
||||
await managedClient.EnqueueAsync("5");
|
||||
await managedClient.EnqueueAsync("6");
|
||||
await managedClient.EnqueueAsync("7");
|
||||
await managedClient.EnqueueAsync("8");
|
||||
|
||||
Assert.AreEqual(5, managedClient.PendingApplicationMessagesCount);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await managedClient.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ManagedClients_Will_Message_Send()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessagesCount = 0;
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort)
|
||||
.WithWillTopic("My/last/will")
|
||||
.WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce)
|
||||
.Build();
|
||||
|
||||
var dyingClient = testEnvironment.CreateClient();
|
||||
var dyingManagedClient = new MQTTnet.Extensions.ManagedClient.ManagedMqttClient(dyingClient, testEnvironment.ClientLogger);
|
||||
|
||||
await dyingManagedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).Build());
|
||||
|
||||
// Wait until the managed client is fully set up and running.
|
||||
await Task.Delay(1000);
|
||||
|
||||
var receivingClient = await testEnvironment.ConnectClient();
|
||||
|
||||
receivingClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Interlocked.Increment(ref receivedMessagesCount);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await receivingClient.SubscribeAsync("My/last/will");
|
||||
|
||||
// Disposing the client will not sent a DISCONNECT packet so that connection is terminated
|
||||
// which will lead to the will publish.
|
||||
dyingManagedClient.Dispose();
|
||||
|
||||
// Wait for arrival of the will message at the receiver.
|
||||
await Task.Delay(5000);
|
||||
|
||||
Assert.AreEqual(1, receivedMessagesCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Start_Stop()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
var managedClient = new MQTTnet.Extensions.ManagedClient.ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort);
|
||||
|
||||
var connected = GetConnectedTask(managedClient);
|
||||
|
||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).Build());
|
||||
|
||||
await connected;
|
||||
|
||||
await managedClient.StopAsync();
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(0, (await server.GetClientsAsync()).Count);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Storage_Queue_Drains()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
testEnvironment.IgnoreServerLogErrors = true;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var managedClient = new MQTTnet.Extensions.ManagedClient.ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetEventLogger());
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort);
|
||||
var storage = new ManagedMqttClientTestStorage();
|
||||
|
||||
var connected = GetConnectedTask(managedClient);
|
||||
|
||||
await managedClient.StartAsync(
|
||||
new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithStorage(storage).WithAutoReconnectDelay(TimeSpan.FromSeconds(5)).Build());
|
||||
|
||||
await connected;
|
||||
|
||||
await testEnvironment.Server.StopAsync();
|
||||
|
||||
await managedClient.EnqueueAsync("1");
|
||||
|
||||
//Message should have been added to the storage queue in PublishAsync,
|
||||
//and we are awaiting PublishAsync, so the message should already be
|
||||
//in storage at this point (i.e. no waiting).
|
||||
Assert.AreEqual(1, storage.GetMessageCount());
|
||||
|
||||
connected = GetConnectedTask(managedClient);
|
||||
|
||||
await testEnvironment.Server.StartAsync();
|
||||
|
||||
await connected;
|
||||
|
||||
//Wait 500ms here so the client has time to publish the queued message
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(0, storage.GetMessageCount());
|
||||
|
||||
await managedClient.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscriptions_And_Unsubscriptions_Are_Made_And_Reestablished_At_Reconnect()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var unmanagedClient = testEnvironment.CreateClient();
|
||||
var managedClient = await CreateManagedClientAsync(testEnvironment, unmanagedClient);
|
||||
|
||||
var received = SetupReceivingOfMessages(managedClient, 2);
|
||||
|
||||
// Perform some opposing subscriptions and unsubscriptions to verify
|
||||
// that these conflicting subscriptions are handled correctly
|
||||
await managedClient.SubscribeAsync("keptSubscribed");
|
||||
await managedClient.SubscribeAsync("subscribedThenUnsubscribed");
|
||||
|
||||
await managedClient.UnsubscribeAsync("subscribedThenUnsubscribed");
|
||||
await managedClient.UnsubscribeAsync("unsubscribedThenSubscribed");
|
||||
|
||||
await managedClient.SubscribeAsync("unsubscribedThenSubscribed");
|
||||
|
||||
//wait a bit for the subscriptions to become established before the messages are published
|
||||
await Task.Delay(500);
|
||||
|
||||
var sendingClient = await testEnvironment.ConnectClient();
|
||||
|
||||
async Task PublishMessages()
|
||||
{
|
||||
await sendingClient.PublishBinaryAsync("keptSubscribed", new byte[] { 1 });
|
||||
await sendingClient.PublishBinaryAsync("subscribedThenUnsubscribed", new byte[] { 1 });
|
||||
await sendingClient.PublishBinaryAsync("unsubscribedThenSubscribed", new byte[] { 1 });
|
||||
}
|
||||
|
||||
await PublishMessages();
|
||||
|
||||
async Task AssertMessagesReceived()
|
||||
{
|
||||
var messages = await received;
|
||||
Assert.AreEqual("keptSubscribed", messages[0].Topic);
|
||||
Assert.AreEqual("unsubscribedThenSubscribed", messages[1].Topic);
|
||||
}
|
||||
|
||||
await AssertMessagesReceived();
|
||||
|
||||
var connected = GetConnectedTask(managedClient);
|
||||
|
||||
await unmanagedClient.DisconnectAsync();
|
||||
|
||||
// the managed client has to reconnect by itself
|
||||
await connected;
|
||||
|
||||
// wait a bit so that the managed client can reestablish the subscriptions
|
||||
await Task.Delay(500);
|
||||
|
||||
received = SetupReceivingOfMessages(managedClient, 2);
|
||||
|
||||
await PublishMessages();
|
||||
|
||||
// and then the same subscriptions need to exist again
|
||||
await AssertMessagesReceived();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscriptions_Are_Cleared_At_Logout()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer().ConfigureAwait(false);
|
||||
|
||||
var sendingClient = await testEnvironment.ConnectClient().ConfigureAwait(false);
|
||||
await sendingClient.PublishStringAsync("topic", "A", retain: true);
|
||||
|
||||
// Wait a bit for the retained message to be available
|
||||
await LongTestDelay();
|
||||
|
||||
await sendingClient.DisconnectAsync();
|
||||
|
||||
// Now use the managed client and check if subscriptions get cleared properly.
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort);
|
||||
|
||||
var receivedManagedMessages = new List<MqttApplicationMessage>();
|
||||
|
||||
var managedClient = testEnvironment.Factory.CreateManagedMqttClient(testEnvironment.CreateClient());
|
||||
managedClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedManagedMessages.Add(e.ApplicationMessage);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await managedClient.SubscribeAsync("topic");
|
||||
|
||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(0.5)).Build());
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(1, receivedManagedMessages.Count);
|
||||
|
||||
await managedClient.StopAsync();
|
||||
await LongTestDelay();
|
||||
|
||||
await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).WithAutoReconnectDelay(TimeSpan.FromSeconds(0.5)).Build());
|
||||
await LongTestDelay();
|
||||
|
||||
// After reconnect and then some delay, the retained message must not be received,
|
||||
// showing that the subscriptions were cleared
|
||||
Assert.AreEqual(1, receivedManagedMessages.Count);
|
||||
|
||||
// Make sure that it gets received after subscribing again.
|
||||
await managedClient.SubscribeAsync("topic");
|
||||
await LongTestDelay();
|
||||
|
||||
Assert.AreEqual(2, receivedManagedMessages.Count);
|
||||
}
|
||||
}
|
||||
|
||||
// This case also serves as a regression test for the previous behavior
|
||||
// that subscriptions were only published at the ConnectionCheckInterval
|
||||
[TestMethod]
|
||||
public async Task Subscriptions_Are_Published_Immediately()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
// Use a long connection check interval to verify that the subscriptions
|
||||
// do not depend on the connection check interval anymore
|
||||
var connectionCheckInterval = TimeSpan.FromSeconds(10);
|
||||
var receivingClient = await CreateManagedClientAsync(testEnvironment, null, connectionCheckInterval);
|
||||
var sendingClient = await testEnvironment.ConnectClient();
|
||||
|
||||
await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", Payload = new byte[] { 1 }, Retain = true });
|
||||
|
||||
var subscribeTime = DateTime.UtcNow;
|
||||
|
||||
var messagesTask = SetupReceivingOfMessages(receivingClient, 1);
|
||||
|
||||
await receivingClient.SubscribeAsync("topic");
|
||||
|
||||
var messages = await messagesTask;
|
||||
|
||||
var elapsed = DateTime.UtcNow - subscribeTime;
|
||||
Assert.IsTrue(elapsed < TimeSpan.FromSeconds(1), $"Subscriptions must be activated immediately, this one took {elapsed}");
|
||||
Assert.AreEqual(messages.Single().Topic, "topic");
|
||||
}
|
||||
}
|
||||
|
||||
// This case also serves as a regression test for the previous behavior which re-published
|
||||
// each and every existing subscriptions with every new subscription that was made
|
||||
// (causing performance problems and having the visible symptom of retained messages being received again)
|
||||
[TestMethod]
|
||||
public async Task Subscriptions_Subscribe_Only_New_Subscriptions()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
var managedClient = await CreateManagedClientAsync(testEnvironment);
|
||||
|
||||
var sendingClient = await testEnvironment.ConnectClient();
|
||||
|
||||
await managedClient.SubscribeAsync("topic");
|
||||
|
||||
//wait a bit for the subscription to become established
|
||||
await Task.Delay(500);
|
||||
|
||||
await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", Payload = new byte[] { 1 }, Retain = true });
|
||||
|
||||
var messages = await SetupReceivingOfMessages(managedClient, 1);
|
||||
|
||||
Assert.AreEqual(1, messages.Count);
|
||||
Assert.AreEqual("topic", messages.Single().Topic);
|
||||
|
||||
await managedClient.SubscribeAsync("anotherTopic");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
// The subscription of the other topic must not trigger a re-subscription of the existing topic
|
||||
// (and thus renewed receiving of the retained message)
|
||||
Assert.AreEqual(1, messages.Count);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<MQTTnet.Extensions.ManagedClient.ManagedMqttClient> CreateManagedClientAsync(
|
||||
TestEnvironment testEnvironment,
|
||||
IMqttClient underlyingClient = null,
|
||||
TimeSpan? connectionCheckInterval = null,
|
||||
string host = "localhost")
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer(host, testEnvironment.ServerPort);
|
||||
|
||||
var managedOptions = new ManagedMqttClientOptionsBuilder().WithClientOptions(clientOptions).Build();
|
||||
|
||||
// Use a short connection check interval so that subscription operations are performed quickly
|
||||
// in order to verify against a previous implementation that performed subscriptions only
|
||||
// at connection check intervals
|
||||
managedOptions.ConnectionCheckInterval = connectionCheckInterval ?? TimeSpan.FromSeconds(0.1);
|
||||
|
||||
var managedClient = new MQTTnet.Extensions.ManagedClient.ManagedMqttClient(underlyingClient ?? testEnvironment.CreateClient(), new MqttNetEventLogger());
|
||||
|
||||
var connected = GetConnectedTask(managedClient);
|
||||
|
||||
await managedClient.StartAsync(managedOptions);
|
||||
|
||||
await connected;
|
||||
|
||||
return managedClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a task that will finish when the
|
||||
/// <paramref
|
||||
/// name="managedClient" />
|
||||
/// has connected
|
||||
/// </summary>
|
||||
Task GetConnectedTask(MQTTnet.Extensions.ManagedClient.ManagedMqttClient managedClient)
|
||||
{
|
||||
var connected = new TaskCompletionSource<bool>();
|
||||
|
||||
managedClient.ConnectedAsync += e =>
|
||||
{
|
||||
connected.TrySetResult(true);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
return connected.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a task that will return the messages received on
|
||||
/// <paramref
|
||||
/// name="managedClient" />
|
||||
/// when
|
||||
/// <paramref
|
||||
/// name="expectedNumberOfMessages" />
|
||||
/// have been received
|
||||
/// </summary>
|
||||
Task<List<MqttApplicationMessage>> SetupReceivingOfMessages(MQTTnet.Extensions.ManagedClient.ManagedMqttClient managedClient, int expectedNumberOfMessages)
|
||||
{
|
||||
var receivedMessages = new List<MqttApplicationMessage>();
|
||||
var result = new AsyncTaskCompletionSource<List<MqttApplicationMessage>>();
|
||||
|
||||
managedClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedMessages.Add(e.ApplicationMessage);
|
||||
|
||||
if (receivedMessages.Count == expectedNumberOfMessages)
|
||||
{
|
||||
result.TrySetResult(receivedMessages);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
return result.Task;
|
||||
}
|
||||
}
|
||||
|
||||
public class ManagedMqttClientTestStorage : IManagedMqttClientStorage
|
||||
{
|
||||
IList<ManagedMqttApplicationMessage> _messages;
|
||||
|
||||
public int GetMessageCount()
|
||||
{
|
||||
return _messages.Count;
|
||||
}
|
||||
|
||||
public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
|
||||
{
|
||||
if (_messages == null)
|
||||
{
|
||||
_messages = new List<ManagedMqttApplicationMessage>();
|
||||
}
|
||||
|
||||
return Task.FromResult(_messages);
|
||||
}
|
||||
|
||||
public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
|
||||
{
|
||||
_messages = messages;
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// 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;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Tests.Clients.MqttClient
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttClient_Connection_Tests : BaseTestClass
|
||||
{
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(MqttCommunicationException))]
|
||||
public async Task Connect_To_Invalid_Server_Port_Not_Opened()
|
||||
{
|
||||
var client = new MqttFactory().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 MqttFactory().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 MqttFactory().CreateMqttClient();
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("http://127.0.0.1", 12345).WithTimeout(TimeSpan.FromSeconds(2)).Build());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task ConnectTimeout_Throws_Exception()
|
||||
{
|
||||
var factory = new MqttFactory();
|
||||
using (var client = factory.CreateMqttClient())
|
||||
{
|
||||
var disconnectHandlerCalled = false;
|
||||
try
|
||||
{
|
||||
client.DisconnectedAsync += args =>
|
||||
{
|
||||
disconnectHandlerCalled = true;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").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())
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
ClientDisconnectedEventArgs eventArgs = null;
|
||||
server.ClientDisconnectedAsync += args =>
|
||||
{
|
||||
eventArgs = args;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var disconnectOptions = testEnvironment.Factory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
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.Factory.CreateClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.MessageRateTooHigh).Build();
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
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.Factory.CreateClientDisconnectOptionsBuilder().WithUserProperty("test_name", "test_value").Build();
|
||||
|
||||
// Perform a clean disconnect.
|
||||
await client.DisconnectAsync(disconnectOptions);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,928 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace MQTTnet.Tests.Clients.MqttClient
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class MqttClient_Tests : BaseTestClass
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow(MqttQualityOfServiceLevel.ExactlyOnce)]
|
||||
[DataRow(MqttQualityOfServiceLevel.AtMostOnce)]
|
||||
[DataRow(MqttQualityOfServiceLevel.AtLeastOnce)]
|
||||
public async Task Concurrent_Processing(MqttQualityOfServiceLevel qos)
|
||||
{
|
||||
long concurrency = 0;
|
||||
var success = false;
|
||||
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var publisher = await testEnvironment.ConnectClient();
|
||||
var subscriber = await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId(qos.ToString()));
|
||||
await subscriber.SubscribeAsync("#", qos);
|
||||
|
||||
subscriber.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
e.AutoAcknowledge = false;
|
||||
|
||||
async Task InvokeInternal()
|
||||
{
|
||||
if (Interlocked.Increment(ref concurrency) > 1)
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
|
||||
await Task.Delay(100);
|
||||
Interlocked.Decrement(ref concurrency);
|
||||
}
|
||||
|
||||
_ = InvokeInternal();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var publishes = Task.WhenAll(publisher.PublishStringAsync("a", null, qos), publisher.PublishStringAsync("b", null, qos));
|
||||
|
||||
await Task.Delay(200);
|
||||
|
||||
await publishes;
|
||||
Assert.IsTrue(success);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Connect_Disconnect_Connect()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var clientOptions = testEnvironment.CreateDefaultClientOptions();
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
await client.ConnectAsync(clientOptions);
|
||||
await client.DisconnectAsync();
|
||||
await client.ConnectAsync(clientOptions);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public async Task Connect_Multiple_Times_Should_Fail()
|
||||
{
|
||||
using (var testEnvironment = CreateTestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var clientOptions = testEnvironment.CreateDefaultClientOptions();
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
await client.ConnectAsync(clientOptions);
|
||||
await client.ConnectAsync(clientOptions);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Disconnect_Event_Contains_Exception()
|
||||
{
|
||||
var factory = new MqttFactory();
|
||||
using (var client = factory.CreateMqttClient())
|
||||
{
|
||||
Exception ex = null;
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
ex = e.Exception;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsNotNull(ex);
|
||||
Assert.IsInstanceOfType(ex, typeof(MqttCommunicationException));
|
||||
Assert.IsInstanceOfType(ex.InnerException, typeof(SocketException));
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Ensure_Queue_Drain()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectLowLevelClient();
|
||||
|
||||
var i = 0;
|
||||
server.InterceptingPublishAsync += c =>
|
||||
{
|
||||
i++;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.SendAsync(
|
||||
new MqttConnectPacket
|
||||
{
|
||||
ClientId = "Ensure_Queue_Drain_Test"
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await client.SendAsync(
|
||||
new MqttPublishPacket
|
||||
{
|
||||
Topic = "Test"
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
// This will simulate a device which closes the connection directly
|
||||
// after sending the data so do delay is added between send and dispose!
|
||||
client.Dispose();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
Assert.AreEqual(1, i);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Fire_Disconnected_Event_On_Server_Shutdown()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var handlerFired = false;
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
handlerFired = true;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await server.StopAsync();
|
||||
|
||||
await Task.Delay(4000);
|
||||
|
||||
Assert.IsTrue(handlerFired);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Frequent_Connects()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var clients = new List<IMqttClient>();
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
clients.Add(await testEnvironment.ConnectClient(new MqttClientOptionsBuilder().WithClientId("a")));
|
||||
}
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
var clientStatus = await testEnvironment.Server.GetClientsAsync();
|
||||
var sessionStatus = await testEnvironment.Server.GetSessionsAsync();
|
||||
|
||||
for (var i = 0; i < 98; i++)
|
||||
{
|
||||
Assert.IsFalse(clients[i].TryPingAsync().GetAwaiter().GetResult(), $"clients[{i}] is not connected");
|
||||
}
|
||||
|
||||
Assert.IsTrue(clients[99].TryPingAsync().GetAwaiter().GetResult());
|
||||
|
||||
Assert.AreEqual(1, clientStatus.Count);
|
||||
Assert.AreEqual(1, sessionStatus.Count);
|
||||
|
||||
var receiveClient = clients[99];
|
||||
object receivedPayload = null;
|
||||
receiveClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await receiveClient.SubscribeAsync("x");
|
||||
|
||||
var sendClient = await testEnvironment.ConnectClient();
|
||||
await sendClient.PublishStringAsync("x", "1");
|
||||
|
||||
await Task.Delay(250);
|
||||
|
||||
Assert.AreEqual("1", receivedPayload);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Invalid_Connect_Throws_Exception()
|
||||
{
|
||||
var factory = new MqttFactory();
|
||||
using (var client = factory.CreateMqttClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
|
||||
|
||||
Assert.Fail("Must fail!");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Assert.IsNotNull(exception);
|
||||
Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
|
||||
Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task No_Payload()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var sender = await testEnvironment.ConnectClient();
|
||||
var receiver = await testEnvironment.ConnectClient();
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("A");
|
||||
|
||||
await receiver.SubscribeAsync(
|
||||
new MqttClientSubscribeOptions
|
||||
{
|
||||
TopicFilters = new List<MqttTopicFilter> { new MqttTopicFilter { Topic = "#" } }
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
||||
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(null, receivedMessage.Payload);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task NoConnectedHandler_Connect_DoesNotThrowException()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task NoDisconnectedHandler_Disconnect_DoesNotThrowException()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
|
||||
await client.DisconnectAsync();
|
||||
|
||||
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task PacketIdentifier_In_Publish_Result()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
var result = await client.PublishStringAsync("a", "a");
|
||||
Assert.AreEqual(null, result.PacketIdentifier);
|
||||
|
||||
result = await client.PublishStringAsync("b", "b");
|
||||
Assert.AreEqual(null, result.PacketIdentifier);
|
||||
|
||||
result = await client.PublishStringAsync("a", "a", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
Assert.AreEqual((ushort)1, result.PacketIdentifier);
|
||||
|
||||
result = await client.PublishStringAsync("b", "b", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
Assert.AreEqual((ushort)2, result.PacketIdentifier);
|
||||
|
||||
result = await client.PublishStringAsync("a", "a", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
Assert.AreEqual((ushort)3, result.PacketIdentifier);
|
||||
|
||||
result = await client.PublishStringAsync("b", "b", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
Assert.AreEqual((ushort)4, result.PacketIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Preserve_Message_Order()
|
||||
{
|
||||
// The messages are sent in reverse or to ensure that the delay in the handler
|
||||
// needs longer for the first messages and later messages may be processed earlier (if there
|
||||
// is an issue).
|
||||
const int MessagesCount = 50;
|
||||
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("x");
|
||||
|
||||
var receivedValues = new List<int>();
|
||||
|
||||
async Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
var value = int.Parse(eventArgs.ApplicationMessage.ConvertPayloadToString());
|
||||
await Task.Delay(value);
|
||||
|
||||
lock (receivedValues)
|
||||
{
|
||||
receivedValues.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
client1.ApplicationMessageReceivedAsync += Handler1;
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
for (var i = MessagesCount; i > 0; i--)
|
||||
{
|
||||
await client2.PublishStringAsync("x", i.ToString());
|
||||
}
|
||||
|
||||
await Task.Delay(5000);
|
||||
|
||||
for (var i = MessagesCount; i > 0; i--)
|
||||
{
|
||||
Assert.AreEqual(i, receivedValues[MessagesCount - i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Preserve_Message_Order_With_Delayed_Acknowledgement()
|
||||
{
|
||||
// The messages are sent in reverse or to ensure that the delay in the handler
|
||||
// needs longer for the first messages and later messages may be processed earlier (if there
|
||||
// is an issue).
|
||||
const int MessagesCount = 50;
|
||||
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("x", MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
|
||||
var receivedValues = new List<int>();
|
||||
|
||||
Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
var value = int.Parse(eventArgs.ApplicationMessage.ConvertPayloadToString());
|
||||
eventArgs.AutoAcknowledge = false;
|
||||
Task.Delay(value).ContinueWith(x => eventArgs.AcknowledgeAsync(CancellationToken.None));
|
||||
|
||||
Debug.WriteLine($"received {value}");
|
||||
lock (receivedValues)
|
||||
{
|
||||
receivedValues.Add(value);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
client1.ApplicationMessageReceivedAsync += Handler1;
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
for (var i = MessagesCount; i > 0; i--)
|
||||
{
|
||||
await client2.PublishStringAsync("x", i.ToString(), MqttQualityOfServiceLevel.ExactlyOnce);
|
||||
}
|
||||
|
||||
await Task.Delay(5000);
|
||||
|
||||
for (var i = MessagesCount; i > 0; i--)
|
||||
{
|
||||
Assert.AreEqual(i, receivedValues[MessagesCount - i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_0_Over_Period_Exceeding_KeepAlive()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
const int KeepAlivePeriodSecs = 3;
|
||||
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var options = new MqttClientOptionsBuilder().WithKeepAlivePeriod(TimeSpan.FromSeconds(KeepAlivePeriodSecs));
|
||||
var client = await testEnvironment.ConnectClient(options);
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").Build();
|
||||
|
||||
try
|
||||
{
|
||||
// Publish messages over a time period exceeding the keep alive period.
|
||||
// This should not cause an exception because of, i.e. "Client disconnected".
|
||||
|
||||
for (var count = 0; count < KeepAlivePeriodSecs * 3; ++count)
|
||||
{
|
||||
// Send Publish requests well before the keep alive period expires
|
||||
await client.PublishAsync(message);
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Assert.Fail(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_QoS_1_In_ApplicationMessageReceiveHandler()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
const string client1Topic = "client1/topic";
|
||||
const string client2Topic = "client2/topic";
|
||||
const string expectedClient2Message = "hello client2";
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
client1.ApplicationMessageReceivedAsync += async e =>
|
||||
{
|
||||
await client1.PublishStringAsync(client2Topic, expectedClient2Message, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
};
|
||||
|
||||
await client1.SubscribeAsync(client1Topic, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
|
||||
var client2TopicResults = new List<string>();
|
||||
|
||||
client2.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
client2TopicResults.Add(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client2.SubscribeAsync(client2Topic);
|
||||
|
||||
var client3 = await testEnvironment.ConnectClient();
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic(client1Topic).Build();
|
||||
await client3.PublishAsync(message);
|
||||
await client3.PublishAsync(message);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(2, client2TopicResults.Count);
|
||||
Assert.AreEqual(expectedClient2Message, client2TopicResults[0]);
|
||||
Assert.AreEqual(expectedClient2Message, client2TopicResults[1]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Publish_With_Correct_Retain_Flag()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = new List<MqttApplicationMessage>();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
client1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
lock (receivedMessages)
|
||||
{
|
||||
receivedMessages.Add(e.ApplicationMessage);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client1.SubscribeAsync("a");
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithRetainFlag().Build();
|
||||
await client2.PublishAsync(message);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(1, receivedMessages.Count);
|
||||
Assert.IsFalse(receivedMessages.First().Retain); // Must be false even if set above!
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Reconnect()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.IsTrue(await client.TryPingAsync());
|
||||
|
||||
await server.StopAsync();
|
||||
await Task.Delay(500);
|
||||
Assert.IsFalse(await client.TryPingAsync());
|
||||
|
||||
await server.StartAsync();
|
||||
await Task.Delay(500);
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
Assert.IsTrue(await client.TryPingAsync());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Reconnect_From_Disconnected_Event()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
var tries = 0;
|
||||
var maxTries = 3;
|
||||
|
||||
client.DisconnectedAsync += async e =>
|
||||
{
|
||||
if (tries >= maxTries)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref tries);
|
||||
|
||||
await Task.Delay(100);
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
Assert.Fail("Must fail!");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
SpinWait.SpinUntil(() => tries >= maxTries, 10000);
|
||||
|
||||
Assert.AreEqual(maxTries, tries);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Reconnect_While_Server_Offline()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
testEnvironment.IgnoreClientLogErrors = true;
|
||||
|
||||
var server = await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
await Task.Delay(500);
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
|
||||
await server.StopAsync();
|
||||
await Task.Delay(500);
|
||||
Assert.IsFalse(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
Assert.Fail("Must fail!");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
await server.StartAsync();
|
||||
await Task.Delay(500);
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
|
||||
await client.PingAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Send_Manual_Ping()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
await client.PingAsync(CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Send_Reply_For_Any_Received_Message()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
await client1.SubscribeAsync("request/+");
|
||||
|
||||
async Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
await client1.PublishStringAsync($"reply/{eventArgs.ApplicationMessage.Topic}");
|
||||
}
|
||||
|
||||
client1.ApplicationMessageReceivedAsync += Handler1;
|
||||
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
await client2.SubscribeAsync("reply/#");
|
||||
|
||||
var replies = new List<string>();
|
||||
|
||||
Task Handler2(MqttApplicationMessageReceivedEventArgs eventArgs)
|
||||
{
|
||||
lock (replies)
|
||||
{
|
||||
replies.Add(eventArgs.ApplicationMessage.Topic);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
client2.ApplicationMessageReceivedAsync += Handler2;
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
await client2.PublishStringAsync("request/a");
|
||||
await client2.PublishStringAsync("request/b");
|
||||
await client2.PublishStringAsync("request/c");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual("reply/request/a,reply/request/b,reply/request/c", string.Join(",", replies));
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Send_Reply_In_Message_Handler()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client1 = await testEnvironment.ConnectClient();
|
||||
var client2 = await testEnvironment.ConnectClient();
|
||||
|
||||
await client1.SubscribeAsync("#");
|
||||
await client2.SubscribeAsync("#");
|
||||
|
||||
var replyReceived = false;
|
||||
|
||||
client1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
if (e.ApplicationMessage.Topic == "reply")
|
||||
{
|
||||
replyReceived = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
client2.ApplicationMessageReceivedAsync += async e =>
|
||||
{
|
||||
if (e.ApplicationMessage.Topic == "request")
|
||||
{
|
||||
// Use AtMostOnce here because with QoS 1 or even QoS 2 the process waits for
|
||||
// the ACK etc. The problem is that the SpinUntil below only waits until the
|
||||
// flag is set. It does not wait until the client has sent the ACK
|
||||
await client2.PublishStringAsync("reply");
|
||||
}
|
||||
};
|
||||
|
||||
await client1.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsTrue(replyReceived);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Send_Reply_In_Message_Handler_For_Same_Client()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
await client.SubscribeAsync("#");
|
||||
|
||||
var replyReceived = false;
|
||||
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
if (e.ApplicationMessage.Topic == "request")
|
||||
{
|
||||
#pragma warning disable 4014
|
||||
Task.Run(() => client.PublishStringAsync("reply", null, MqttQualityOfServiceLevel.AtLeastOnce));
|
||||
#pragma warning restore 4014
|
||||
}
|
||||
else
|
||||
{
|
||||
replyReceived = true;
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.PublishStringAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
|
||||
SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
|
||||
|
||||
Assert.IsTrue(replyReceived);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Set_ClientWasConnected_On_ClientDisconnect()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
Assert.IsTrue(e.ClientWasConnected);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.DisconnectAsync();
|
||||
await Task.Delay(200);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Set_ClientWasConnected_On_ServerDisconnect()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
var client = await testEnvironment.ConnectClient();
|
||||
|
||||
Assert.IsTrue(client.TryPingAsync().GetAwaiter().GetResult());
|
||||
client.DisconnectedAsync += e =>
|
||||
{
|
||||
Assert.IsTrue(e.ClientWasConnected);
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await server.StopAsync();
|
||||
await Task.Delay(4000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_In_Callback_Events()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment(TestContext))
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
|
||||
var receivedMessages = new List<MqttApplicationMessage>();
|
||||
|
||||
var client = testEnvironment.CreateClient();
|
||||
|
||||
client.ConnectedAsync += async e =>
|
||||
{
|
||||
await client.SubscribeAsync("RCU/P1/H0001/R0003");
|
||||
|
||||
var msg = new MqttApplicationMessageBuilder().WithPayload("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|")
|
||||
.WithTopic("RCU/P1/H0001/R0003");
|
||||
|
||||
await client.PublishAsync(msg.Build());
|
||||
};
|
||||
|
||||
client.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
lock (receivedMessages)
|
||||
{
|
||||
receivedMessages.Add(e.ApplicationMessage);
|
||||
}
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.AreEqual(1, receivedMessages.Count);
|
||||
Assert.AreEqual("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|", receivedMessages.First().ConvertPayloadToString());
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Subscribe_With_QoS2()
|
||||
{
|
||||
using (var testEnvironment = new TestEnvironment())
|
||||
{
|
||||
await testEnvironment.StartServer();
|
||||
var client1 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
var client2 = await testEnvironment.ConnectClient(o => o.WithProtocolVersion(MqttProtocolVersion.V500));
|
||||
|
||||
var disconnectedFired = false;
|
||||
client1.DisconnectedAsync += c =>
|
||||
{
|
||||
disconnectedFired = true;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
var messageReceived = false;
|
||||
client1.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
messageReceived = true;
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await client1.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("topic1").WithExactlyOnceQoS().Build());
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("topic1")
|
||||
.WithPayload("Hello World")
|
||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
.WithRetainFlag()
|
||||
.Build();
|
||||
|
||||
await client2.PublishAsync(message);
|
||||
await Task.Delay(500);
|
||||
|
||||
Assert.IsTrue(messageReceived);
|
||||
Assert.IsTrue(client1.TryPingAsync().GetAwaiter().GetResult());
|
||||
Assert.IsFalse(disconnectedFired);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// 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.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MQTTnet.Client;
|
||||
|
||||
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();
|
||||
|
||||
Assert.AreEqual("user", options.Credentials.GetUserName(null));
|
||||
Assert.IsTrue(Encoding.UTF8.GetBytes("password").SequenceEqual(options.Credentials.GetPassword(null)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user