25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 

508 satır
20 KiB

  1. using Microsoft.VisualStudio.TestTools.UnitTesting;
  2. using MQTTnet.Client;
  3. using MQTTnet.Client.Connecting;
  4. using MQTTnet.Client.Options;
  5. using MQTTnet.Client.Receiving;
  6. using MQTTnet.Diagnostics;
  7. using MQTTnet.Extensions.ManagedClient;
  8. using MQTTnet.Server;
  9. using MQTTnet.Tests.Mockups;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Threading;
  14. using System.Threading.Tasks;
  15. namespace MQTTnet.Tests
  16. {
  17. [TestClass]
  18. public class ManagedMqttClient_Tests
  19. {
  20. public TestContext TestContext { get; set; }
  21. [TestMethod]
  22. public async Task Drop_New_Messages_On_Full_Queue()
  23. {
  24. var factory = new MqttFactory();
  25. var managedClient = factory.CreateManagedMqttClient();
  26. try
  27. {
  28. var clientOptions = new ManagedMqttClientOptionsBuilder()
  29. .WithMaxPendingMessages(5)
  30. .WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage);
  31. clientOptions.WithClientOptions(o => o.WithTcpServer("localhost"));
  32. await managedClient.StartAsync(clientOptions.Build());
  33. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "1" });
  34. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "2" });
  35. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "3" });
  36. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "4" });
  37. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "5" });
  38. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "6" });
  39. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "7" });
  40. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "8" });
  41. Assert.AreEqual(5, managedClient.PendingApplicationMessagesCount);
  42. }
  43. finally
  44. {
  45. await managedClient.StopAsync();
  46. }
  47. }
  48. [TestMethod]
  49. public async Task ManagedClients_Will_Message_Send()
  50. {
  51. using (var testEnvironment = new TestEnvironment(TestContext))
  52. {
  53. var receivedMessagesCount = 0;
  54. await testEnvironment.StartServerAsync();
  55. var willMessage = new MqttApplicationMessageBuilder().WithTopic("My/last/will").WithAtMostOnceQoS().Build();
  56. var clientOptions = new MqttClientOptionsBuilder()
  57. .WithTcpServer("localhost", testEnvironment.ServerPort)
  58. .WithWillMessage(willMessage);
  59. var dyingClient = testEnvironment.CreateClient();
  60. var dyingManagedClient = new ManagedMqttClient(dyingClient, testEnvironment.ClientLogger);
  61. await dyingManagedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
  62. .WithClientOptions(clientOptions)
  63. .Build());
  64. var recievingClient = await testEnvironment.ConnectClientAsync();
  65. await recievingClient.SubscribeAsync("My/last/will");
  66. recievingClient.UseApplicationMessageReceivedHandler(context => Interlocked.Increment(ref receivedMessagesCount));
  67. dyingManagedClient.Dispose();
  68. await Task.Delay(1000);
  69. Assert.AreEqual(1, receivedMessagesCount);
  70. }
  71. }
  72. [TestMethod]
  73. public async Task Start_Stop()
  74. {
  75. using (var testEnvironment = new TestEnvironment(TestContext))
  76. {
  77. var server = await testEnvironment.StartServerAsync();
  78. var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetLogger());
  79. var clientOptions = new MqttClientOptionsBuilder()
  80. .WithTcpServer("localhost", testEnvironment.ServerPort);
  81. var connected = GetConnectedTask(managedClient);
  82. await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
  83. .WithClientOptions(clientOptions)
  84. .Build());
  85. await connected;
  86. await managedClient.StopAsync();
  87. await Task.Delay(500);
  88. Assert.AreEqual(0, (await server.GetClientStatusAsync()).Count);
  89. }
  90. }
  91. [TestMethod]
  92. public async Task Storage_Queue_Drains()
  93. {
  94. using (var testEnvironment = new TestEnvironment(TestContext))
  95. {
  96. testEnvironment.IgnoreClientLogErrors = true;
  97. testEnvironment.IgnoreServerLogErrors = true;
  98. await testEnvironment.StartServerAsync();
  99. var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetLogger());
  100. var clientOptions = new MqttClientOptionsBuilder()
  101. .WithTcpServer("localhost", testEnvironment.ServerPort);
  102. var storage = new ManagedMqttClientTestStorage();
  103. var connected = GetConnectedTask(managedClient);
  104. await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
  105. .WithClientOptions(clientOptions)
  106. .WithStorage(storage)
  107. .WithAutoReconnectDelay(System.TimeSpan.FromSeconds(5))
  108. .Build());
  109. await connected;
  110. await testEnvironment.Server.StopAsync();
  111. await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "1" });
  112. //Message should have been added to the storage queue in PublishAsync,
  113. //and we are awaiting PublishAsync, so the message should already be
  114. //in storage at this point (i.e. no waiting).
  115. Assert.AreEqual(1, storage.GetMessageCount());
  116. connected = GetConnectedTask(managedClient);
  117. await testEnvironment.Server.StartAsync(new MqttServerOptionsBuilder()
  118. .WithDefaultEndpointPort(testEnvironment.ServerPort).Build());
  119. await connected;
  120. //Wait 500ms here so the client has time to publish the queued message
  121. await Task.Delay(500);
  122. Assert.AreEqual(0, storage.GetMessageCount());
  123. await managedClient.StopAsync();
  124. }
  125. }
  126. [TestMethod]
  127. public async Task Subscriptions_And_Unsubscriptions_Are_Made_And_Reestablished_At_Reconnect()
  128. {
  129. using (var testEnvironment = new TestEnvironment(TestContext))
  130. {
  131. var unmanagedClient = testEnvironment.CreateClient();
  132. var managedClient = await CreateManagedClientAsync(testEnvironment, unmanagedClient);
  133. var received = SetupReceivingOfMessages(managedClient, 2);
  134. // Perform some opposing subscriptions and unsubscriptions to verify
  135. // that these conflicting subscriptions are handled correctly
  136. await managedClient.SubscribeAsync("keptSubscribed");
  137. await managedClient.SubscribeAsync("subscribedThenUnsubscribed");
  138. await managedClient.UnsubscribeAsync("subscribedThenUnsubscribed");
  139. await managedClient.UnsubscribeAsync("unsubscribedThenSubscribed");
  140. await managedClient.SubscribeAsync("unsubscribedThenSubscribed");
  141. //wait a bit for the subscriptions to become established before the messages are published
  142. await Task.Delay(500);
  143. var sendingClient = await testEnvironment.ConnectClientAsync();
  144. async Task PublishMessages()
  145. {
  146. await sendingClient.PublishAsync("keptSubscribed", new byte[] { 1 });
  147. await sendingClient.PublishAsync("subscribedThenUnsubscribed", new byte[] { 1 });
  148. await sendingClient.PublishAsync("unsubscribedThenSubscribed", new byte[] { 1 });
  149. }
  150. await PublishMessages();
  151. async Task AssertMessagesReceived()
  152. {
  153. var messages = await received;
  154. Assert.AreEqual("keptSubscribed", messages[0].Topic);
  155. Assert.AreEqual("unsubscribedThenSubscribed", messages[1].Topic);
  156. }
  157. await AssertMessagesReceived();
  158. var connected = GetConnectedTask(managedClient);
  159. await unmanagedClient.DisconnectAsync();
  160. // the managed client has to reconnect by itself
  161. await connected;
  162. // wait a bit so that the managed client can reestablish the subscriptions
  163. await Task.Delay(500);
  164. received = SetupReceivingOfMessages(managedClient, 2);
  165. await PublishMessages();
  166. // and then the same subscriptions need to exist again
  167. await AssertMessagesReceived();
  168. }
  169. }
  170. // This case also serves as a regression test for the previous behavior which re-published
  171. // each and every existing subscriptions with every new subscription that was made
  172. // (causing performance problems and having the visible symptom of retained messages being received again)
  173. [TestMethod]
  174. public async Task Subscriptions_Subscribe_Only_New_Subscriptions()
  175. {
  176. using (var testEnvironment = new TestEnvironment(TestContext))
  177. {
  178. var managedClient = await CreateManagedClientAsync(testEnvironment);
  179. var sendingClient = await testEnvironment.ConnectClientAsync();
  180. await managedClient.SubscribeAsync("topic");
  181. //wait a bit for the subscription to become established
  182. await Task.Delay(500);
  183. await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", Payload = new byte[] { 1 }, Retain = true });
  184. var messages = await SetupReceivingOfMessages(managedClient, 1);
  185. Assert.AreEqual(1, messages.Count);
  186. Assert.AreEqual("topic", messages.Single().Topic);
  187. await managedClient.SubscribeAsync("anotherTopic");
  188. await Task.Delay(500);
  189. // The subscription of the other topic must not trigger a re-subscription of the existing topic
  190. // (and thus renewed receiving of the retained message)
  191. Assert.AreEqual(1, messages.Count);
  192. }
  193. }
  194. // This case also serves as a regression test for the previous behavior
  195. // that subscriptions were only published at the ConnectionCheckInterval
  196. [TestMethod]
  197. public async Task Subscriptions_Are_Published_Immediately()
  198. {
  199. using (var testEnvironment = new TestEnvironment(TestContext))
  200. {
  201. // Use a long connection check interval to verify that the subscriptions
  202. // do not depend on the connection check interval anymore
  203. var connectionCheckInterval = TimeSpan.FromSeconds(10);
  204. var managedClient = await CreateManagedClientAsync(testEnvironment, null, connectionCheckInterval);
  205. var sendingClient = await testEnvironment.ConnectClientAsync();
  206. await sendingClient.PublishAsync(new MqttApplicationMessage { Topic = "topic", Payload = new byte[] { 1 }, Retain = true });
  207. var subscribeTime = DateTime.UtcNow;
  208. var messagesTask = SetupReceivingOfMessages(managedClient, 1);
  209. await managedClient.SubscribeAsync("topic");
  210. var messages = await messagesTask;
  211. var elapsed = DateTime.UtcNow - subscribeTime;
  212. Assert.IsTrue(elapsed < TimeSpan.FromSeconds(1), $"Subscriptions must be activated immediately, this one took {elapsed}");
  213. Assert.AreEqual(messages.Single().Topic, "topic");
  214. }
  215. }
  216. [TestMethod]
  217. public async Task Subscriptions_Are_Cleared_At_Logout()
  218. {
  219. using (var testEnvironment = new TestEnvironment(TestContext))
  220. {
  221. await testEnvironment.StartServerAsync().ConfigureAwait(false);
  222. var sendingClient = await testEnvironment.ConnectClientAsync().ConfigureAwait(false);
  223. await sendingClient.PublishAsync(new MqttApplicationMessage
  224. {
  225. Topic = "topic",
  226. Payload = new byte[] { 1 },
  227. Retain = true
  228. });
  229. // Wait a bit for the retained message to be available
  230. await Task.Delay(500);
  231. await sendingClient.DisconnectAsync();
  232. // Now use the managed client and check if subscriptions get cleared properly.
  233. var clientOptions = new MqttClientOptionsBuilder()
  234. .WithTcpServer("localhost", testEnvironment.ServerPort);
  235. var receivedManagedMessages = new List<MqttApplicationMessage>();
  236. var managedClient = new ManagedMqttClient(testEnvironment.CreateClient(), new MqttNetLogger());
  237. managedClient.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(c =>
  238. {
  239. receivedManagedMessages.Add(c.ApplicationMessage);
  240. });
  241. await managedClient.SubscribeAsync("topic");
  242. await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
  243. .WithClientOptions(clientOptions)
  244. .WithAutoReconnectDelay(TimeSpan.FromSeconds(1))
  245. .Build());
  246. await Task.Delay(500);
  247. Assert.AreEqual(1, receivedManagedMessages.Count);
  248. await managedClient.StopAsync();
  249. await Task.Delay(500);
  250. await managedClient.StartAsync(new ManagedMqttClientOptionsBuilder()
  251. .WithClientOptions(clientOptions)
  252. .WithAutoReconnectDelay(TimeSpan.FromSeconds(1))
  253. .Build());
  254. await Task.Delay(1000);
  255. // After reconnect and then some delay, the retained message must not be received,
  256. // showing that the subscriptions were cleared
  257. Assert.AreEqual(1, receivedManagedMessages.Count);
  258. // Make sure that it gets received after subscribing again.
  259. await managedClient.SubscribeAsync("topic");
  260. await Task.Delay(500);
  261. Assert.AreEqual(2, receivedManagedMessages.Count);
  262. }
  263. }
  264. [TestMethod]
  265. public async Task Manage_Session_MaxParallel_Subscribe()
  266. {
  267. using (var testEnvironment = new TestEnvironment())
  268. {
  269. testEnvironment.IgnoreClientLogErrors = true;
  270. var serverOptions = new MqttServerOptionsBuilder();
  271. await testEnvironment.StartServerAsync(serverOptions);
  272. var options = new MqttClientOptionsBuilder().WithClientId("1").WithKeepAlivePeriod(TimeSpan.FromSeconds(5));
  273. var hasReceive = false;
  274. void OnReceive()
  275. {
  276. if (!hasReceive)
  277. {
  278. hasReceive = true;
  279. }
  280. }
  281. var clients = await Task.WhenAll(Enumerable.Range(0, 25)
  282. .Select(i => TryConnect_Subscribe(testEnvironment, options, OnReceive)));
  283. var connectedClients = clients.Where(c => c?.IsConnected ?? false).ToList();
  284. Assert.AreEqual(1, connectedClients.Count);
  285. await Task.Delay(10000); // over 1.5T
  286. var option2 = new MqttClientOptionsBuilder().WithClientId("2").WithKeepAlivePeriod(TimeSpan.FromSeconds(10));
  287. var sendClient = await testEnvironment.ConnectClientAsync(option2);
  288. await sendClient.PublishAsync("aaa", "1");
  289. await Task.Delay(3000);
  290. Assert.AreEqual(true, hasReceive);
  291. }
  292. }
  293. private async Task<IMqttClient> TryConnect_Subscribe(TestEnvironment testEnvironment, MqttClientOptionsBuilder options, Action onReceive)
  294. {
  295. try
  296. {
  297. var sendClient = await testEnvironment.ConnectClientAsync(options);
  298. sendClient.ApplicationMessageReceivedHandler = new MQTTnet.Client.Receiving.MqttApplicationMessageReceivedHandlerDelegate(e =>
  299. {
  300. onReceive();
  301. });
  302. await sendClient.SubscribeAsync("aaa");
  303. return sendClient;
  304. }
  305. catch (System.Exception)
  306. {
  307. return null;
  308. }
  309. }
  310. async Task<ManagedMqttClient> CreateManagedClientAsync(
  311. TestEnvironment testEnvironment,
  312. IMqttClient underlyingClient = null,
  313. TimeSpan? connectionCheckInterval = null)
  314. {
  315. await testEnvironment.StartServerAsync();
  316. var clientOptions = new MqttClientOptionsBuilder()
  317. .WithTcpServer("localhost", testEnvironment.ServerPort);
  318. var managedOptions = new ManagedMqttClientOptionsBuilder()
  319. .WithClientOptions(clientOptions)
  320. .Build();
  321. // Use a short connection check interval so that subscription operations are performed quickly
  322. // in order to verify against a previous implementation that performed subscriptions only
  323. // at connection check intervals
  324. managedOptions.ConnectionCheckInterval = connectionCheckInterval ?? TimeSpan.FromSeconds(0.1);
  325. var managedClient = new ManagedMqttClient(underlyingClient ?? testEnvironment.CreateClient(), new MqttNetLogger());
  326. var connected = GetConnectedTask(managedClient);
  327. await managedClient.StartAsync(managedOptions);
  328. await connected;
  329. return managedClient;
  330. }
  331. /// <summary>
  332. /// Returns a task that will finish when the <paramref name="managedClient"/> has connected
  333. /// </summary>
  334. Task GetConnectedTask(ManagedMqttClient managedClient)
  335. {
  336. TaskCompletionSource<bool> connected = new TaskCompletionSource<bool>();
  337. managedClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(e =>
  338. {
  339. managedClient.ConnectedHandler = null;
  340. connected.SetResult(true);
  341. });
  342. return connected.Task;
  343. }
  344. /// <summary>
  345. /// Returns a task that will return the messages received on <paramref name="managedClient"/>
  346. /// when <paramref name="expectedNumberOfMessages"/> have been received
  347. /// </summary>
  348. Task<List<MqttApplicationMessage>> SetupReceivingOfMessages(ManagedMqttClient managedClient, int expectedNumberOfMessages)
  349. {
  350. var receivedMessages = new List<MqttApplicationMessage>();
  351. var result = new TaskCompletionSource<List<MqttApplicationMessage>>(TaskCreationOptions.RunContinuationsAsynchronously);
  352. managedClient.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(r =>
  353. {
  354. receivedMessages.Add(r.ApplicationMessage);
  355. if (receivedMessages.Count == expectedNumberOfMessages)
  356. {
  357. result.TrySetResult(receivedMessages);
  358. }
  359. });
  360. return result.Task;
  361. }
  362. }
  363. public class ManagedMqttClientTestStorage : IManagedMqttClientStorage
  364. {
  365. IList<ManagedMqttApplicationMessage> _messages;
  366. public Task<IList<ManagedMqttApplicationMessage>> LoadQueuedMessagesAsync()
  367. {
  368. if (_messages == null)
  369. {
  370. _messages = new List<ManagedMqttApplicationMessage>();
  371. }
  372. return Task.FromResult(_messages);
  373. }
  374. public Task SaveQueuedMessagesAsync(IList<ManagedMqttApplicationMessage> messages)
  375. {
  376. _messages = messages;
  377. return Task.FromResult(0);
  378. }
  379. public int GetMessageCount()
  380. {
  381. return _messages.Count;
  382. }
  383. }
  384. }