You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

624 lines
22 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.Sockets;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Microsoft.VisualStudio.TestTools.UnitTesting;
  8. using MQTTnet.Client;
  9. using MQTTnet.Client.Connecting;
  10. using MQTTnet.Client.Disconnecting;
  11. using MQTTnet.Client.Options;
  12. using MQTTnet.Client.Subscribing;
  13. using MQTTnet.Exceptions;
  14. using MQTTnet.Protocol;
  15. using MQTTnet.Server;
  16. using MQTTnet.Tests.Mockups;
  17. namespace MQTTnet.Tests
  18. {
  19. [TestClass]
  20. public class Client_Tests
  21. {
  22. public TestContext TestContext { get; set; }
  23. [TestMethod]
  24. public async Task Send_Reply_In_Message_Handler_For_Same_Client()
  25. {
  26. using (var testEnvironment = new TestEnvironment(TestContext))
  27. {
  28. await testEnvironment.StartServerAsync();
  29. var client = await testEnvironment.ConnectClientAsync();
  30. await client.SubscribeAsync("#");
  31. var replyReceived = false;
  32. client.UseApplicationMessageReceivedHandler(c =>
  33. {
  34. if (c.ApplicationMessage.Topic == "request")
  35. {
  36. #pragma warning disable 4014
  37. Task.Run(() => client.PublishAsync("reply", null, MqttQualityOfServiceLevel.AtLeastOnce));
  38. #pragma warning restore 4014
  39. }
  40. else
  41. {
  42. replyReceived = true;
  43. }
  44. });
  45. await client.PublishAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
  46. SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
  47. Assert.IsTrue(replyReceived);
  48. }
  49. }
  50. [TestMethod]
  51. public async Task Send_Reply_In_Message_Handler()
  52. {
  53. using (var testEnvironment = new TestEnvironment(TestContext))
  54. {
  55. await testEnvironment.StartServerAsync();
  56. var client1 = await testEnvironment.ConnectClientAsync();
  57. var client2 = await testEnvironment.ConnectClientAsync();
  58. await client1.SubscribeAsync("#");
  59. await client2.SubscribeAsync("#");
  60. var replyReceived = false;
  61. client1.UseApplicationMessageReceivedHandler(c =>
  62. {
  63. if (c.ApplicationMessage.Topic == "reply")
  64. {
  65. replyReceived = true;
  66. }
  67. });
  68. client2.UseApplicationMessageReceivedHandler(async c =>{ await client2.PublishAsync("reply", null, MqttQualityOfServiceLevel.AtLeastOnce); });
  69. await client1.PublishAsync("request", null, MqttQualityOfServiceLevel.AtLeastOnce);
  70. SpinWait.SpinUntil(() => replyReceived, TimeSpan.FromSeconds(10));
  71. Assert.IsTrue(replyReceived);
  72. }
  73. }
  74. [TestMethod]
  75. public async Task Reconnect()
  76. {
  77. using (var testEnvironment = new TestEnvironment(TestContext))
  78. {
  79. var server = await testEnvironment.StartServerAsync();
  80. var client = await testEnvironment.ConnectClientAsync();
  81. await Task.Delay(500);
  82. Assert.IsTrue(client.IsConnected);
  83. await server.StopAsync();
  84. await Task.Delay(500);
  85. Assert.IsFalse(client.IsConnected);
  86. await server.StartAsync(new MqttServerOptionsBuilder().WithDefaultEndpointPort(testEnvironment.ServerPort).Build());
  87. await Task.Delay(500);
  88. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
  89. Assert.IsTrue(client.IsConnected);
  90. }
  91. }
  92. [TestMethod]
  93. public async Task Reconnect_While_Server_Offline()
  94. {
  95. using (var testEnvironment = new TestEnvironment(TestContext))
  96. {
  97. testEnvironment.IgnoreClientLogErrors = true;
  98. var server = await testEnvironment.StartServerAsync();
  99. var client = await testEnvironment.ConnectClientAsync();
  100. await Task.Delay(500);
  101. Assert.IsTrue(client.IsConnected);
  102. await server.StopAsync();
  103. await Task.Delay(500);
  104. Assert.IsFalse(client.IsConnected);
  105. for (var i = 0; i < 5; i++)
  106. {
  107. try
  108. {
  109. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
  110. Assert.Fail("Must fail!");
  111. }
  112. catch
  113. {
  114. }
  115. }
  116. await server.StartAsync(new MqttServerOptionsBuilder().WithDefaultEndpointPort(testEnvironment.ServerPort).Build());
  117. await Task.Delay(500);
  118. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
  119. Assert.IsTrue(client.IsConnected);
  120. }
  121. }
  122. [TestMethod]
  123. public async Task Reconnect_From_Disconnected_Event()
  124. {
  125. using (var testEnvironment = new TestEnvironment(TestContext))
  126. {
  127. testEnvironment.IgnoreClientLogErrors = true;
  128. var client = testEnvironment.CreateClient();
  129. var tries = 0;
  130. var maxTries = 3;
  131. client.UseDisconnectedHandler(async e =>
  132. {
  133. if (tries >= maxTries)
  134. {
  135. return;
  136. }
  137. Interlocked.Increment(ref tries);
  138. await Task.Delay(100);
  139. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
  140. });
  141. try
  142. {
  143. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1", testEnvironment.ServerPort).Build());
  144. Assert.Fail("Must fail!");
  145. }
  146. catch
  147. {
  148. }
  149. SpinWait.SpinUntil(() => tries >= maxTries, 10000);
  150. Assert.AreEqual(maxTries, tries);
  151. }
  152. }
  153. [TestMethod]
  154. public async Task PacketIdentifier_In_Publish_Result()
  155. {
  156. using (var testEnvironment = new TestEnvironment(TestContext))
  157. {
  158. await testEnvironment.StartServerAsync();
  159. var client = await testEnvironment.ConnectClientAsync();
  160. var result = await client.PublishAsync("a", "a", MqttQualityOfServiceLevel.AtMostOnce);
  161. Assert.AreEqual(null, result.PacketIdentifier);
  162. result = await client.PublishAsync("b", "b", MqttQualityOfServiceLevel.AtMostOnce);
  163. Assert.AreEqual(null, result.PacketIdentifier);
  164. result = await client.PublishAsync("a", "a", MqttQualityOfServiceLevel.AtLeastOnce);
  165. Assert.AreEqual((ushort)1, result.PacketIdentifier);
  166. result = await client.PublishAsync("b", "b", MqttQualityOfServiceLevel.AtLeastOnce);
  167. Assert.AreEqual((ushort)2, result.PacketIdentifier);
  168. result = await client.PublishAsync("a", "a", MqttQualityOfServiceLevel.ExactlyOnce);
  169. Assert.AreEqual((ushort)3, result.PacketIdentifier);
  170. result = await client.PublishAsync("b", "b", MqttQualityOfServiceLevel.ExactlyOnce);
  171. Assert.AreEqual((ushort)4, result.PacketIdentifier);
  172. }
  173. }
  174. [TestMethod]
  175. public async Task Invalid_Connect_Throws_Exception()
  176. {
  177. var factory = new MqttFactory();
  178. using (var client = factory.CreateMqttClient())
  179. {
  180. try
  181. {
  182. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
  183. Assert.Fail("Must fail!");
  184. }
  185. catch (Exception exception)
  186. {
  187. Assert.IsNotNull(exception);
  188. Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
  189. Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
  190. }
  191. }
  192. }
  193. [TestMethod]
  194. public async Task ConnectTimeout_Throws_Exception()
  195. {
  196. var factory = new MqttFactory();
  197. using (var client = factory.CreateMqttClient())
  198. {
  199. bool disconnectHandlerCalled = false;
  200. try
  201. {
  202. client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(args =>
  203. {
  204. disconnectHandlerCalled = true;
  205. });
  206. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("1.2.3.4").Build());
  207. Assert.Fail("Must fail!");
  208. }
  209. catch (Exception exception)
  210. {
  211. Assert.IsNotNull(exception);
  212. Assert.IsInstanceOfType(exception, typeof(MqttCommunicationException));
  213. //Assert.IsInstanceOfType(exception.InnerException, typeof(SocketException));
  214. }
  215. await Task.Delay(100); // disconnected handler is called async
  216. Assert.IsTrue(disconnectHandlerCalled);
  217. }
  218. }
  219. [TestMethod]
  220. public async Task Fire_Disconnected_Event_On_Server_Shutdown()
  221. {
  222. using (var testEnvironment = new TestEnvironment(TestContext))
  223. {
  224. var server = await testEnvironment.StartServerAsync();
  225. var client = await testEnvironment.ConnectClientAsync();
  226. var handlerFired = false;
  227. client.UseDisconnectedHandler(e => handlerFired = true);
  228. await server.StopAsync();
  229. await Task.Delay(4000);
  230. Assert.IsTrue(handlerFired);
  231. }
  232. }
  233. [TestMethod]
  234. public async Task Disconnect_Event_Contains_Exception()
  235. {
  236. var factory = new MqttFactory();
  237. using (var client = factory.CreateMqttClient())
  238. {
  239. Exception ex = null;
  240. client.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(e =>
  241. {
  242. ex = e.Exception;
  243. });
  244. try
  245. {
  246. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("wrong-server").Build());
  247. }
  248. catch
  249. {
  250. }
  251. await Task.Delay(500);
  252. Assert.IsNotNull(ex);
  253. Assert.IsInstanceOfType(ex, typeof(MqttCommunicationException));
  254. Assert.IsInstanceOfType(ex.InnerException, typeof(SocketException));
  255. }
  256. }
  257. [TestMethod]
  258. public async Task Preserve_Message_Order()
  259. {
  260. // The messages are sent in reverse or to ensure that the delay in the handler
  261. // needs longer for the first messages and later messages may be processed earlier (if there
  262. // is an issue).
  263. const int MessagesCount = 50;
  264. using (var testEnvironment = new TestEnvironment(TestContext))
  265. {
  266. await testEnvironment.StartServerAsync();
  267. var client1 = await testEnvironment.ConnectClientAsync();
  268. await client1.SubscribeAsync("x");
  269. var receivedValues = new List<int>();
  270. async Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
  271. {
  272. var value = int.Parse(eventArgs.ApplicationMessage.ConvertPayloadToString());
  273. await Task.Delay(value);
  274. lock (receivedValues)
  275. {
  276. receivedValues.Add(value);
  277. }
  278. }
  279. client1.UseApplicationMessageReceivedHandler(Handler1);
  280. var client2 = await testEnvironment.ConnectClientAsync();
  281. for (var i = MessagesCount; i > 0; i--)
  282. {
  283. await client2.PublishAsync("x", i.ToString());
  284. }
  285. await Task.Delay(5000);
  286. for (var i = MessagesCount; i > 0; i--)
  287. {
  288. Assert.AreEqual(i, receivedValues[MessagesCount - i]);
  289. }
  290. }
  291. }
  292. [TestMethod]
  293. public async Task Send_Reply_For_Any_Received_Message()
  294. {
  295. using (var testEnvironment = new TestEnvironment(TestContext))
  296. {
  297. await testEnvironment.StartServerAsync();
  298. var client1 = await testEnvironment.ConnectClientAsync();
  299. await client1.SubscribeAsync("request/+");
  300. async Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
  301. {
  302. await client1.PublishAsync($"reply/{eventArgs.ApplicationMessage.Topic}");
  303. }
  304. client1.UseApplicationMessageReceivedHandler(Handler1);
  305. var client2 = await testEnvironment.ConnectClientAsync();
  306. await client2.SubscribeAsync("reply/#");
  307. var replies = new List<string>();
  308. void Handler2(MqttApplicationMessageReceivedEventArgs eventArgs)
  309. {
  310. lock (replies)
  311. {
  312. replies.Add(eventArgs.ApplicationMessage.Topic);
  313. }
  314. }
  315. client2.UseApplicationMessageReceivedHandler((Action<MqttApplicationMessageReceivedEventArgs>)Handler2);
  316. await Task.Delay(500);
  317. await client2.PublishAsync("request/a");
  318. await client2.PublishAsync("request/b");
  319. await client2.PublishAsync("request/c");
  320. await Task.Delay(500);
  321. Assert.AreEqual("reply/request/a,reply/request/b,reply/request/c", string.Join(",", replies));
  322. }
  323. }
  324. [TestMethod]
  325. public async Task Publish_With_Correct_Retain_Flag()
  326. {
  327. using (var testEnvironment = new TestEnvironment(TestContext))
  328. {
  329. await testEnvironment.StartServerAsync();
  330. var receivedMessages = new List<MqttApplicationMessage>();
  331. var client1 = await testEnvironment.ConnectClientAsync();
  332. client1.UseApplicationMessageReceivedHandler(c =>
  333. {
  334. lock (receivedMessages)
  335. {
  336. receivedMessages.Add(c.ApplicationMessage);
  337. }
  338. });
  339. await client1.SubscribeAsync("a");
  340. var client2 = await testEnvironment.ConnectClientAsync();
  341. var message = new MqttApplicationMessageBuilder().WithTopic("a").WithRetainFlag().Build();
  342. await client2.PublishAsync(message);
  343. await Task.Delay(500);
  344. Assert.AreEqual(1, receivedMessages.Count);
  345. Assert.IsFalse(receivedMessages.First().Retain); // Must be false even if set above!
  346. }
  347. }
  348. [TestMethod]
  349. public async Task Subscribe_In_Callback_Events()
  350. {
  351. using (var testEnvironment = new TestEnvironment(TestContext))
  352. {
  353. await testEnvironment.StartServerAsync();
  354. var receivedMessages = new List<MqttApplicationMessage>();
  355. var client = testEnvironment.CreateClient();
  356. client.ConnectedHandler = new MqttClientConnectedHandlerDelegate(async e =>
  357. {
  358. await client.SubscribeAsync("RCU/P1/H0001/R0003");
  359. var msg = new MqttApplicationMessageBuilder()
  360. .WithPayload("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|")
  361. .WithTopic("RCU/P1/H0001/R0003");
  362. await client.PublishAsync(msg.Build());
  363. });
  364. client.UseApplicationMessageReceivedHandler(c =>
  365. {
  366. lock (receivedMessages)
  367. {
  368. receivedMessages.Add(c.ApplicationMessage);
  369. }
  370. });
  371. await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).Build());
  372. await Task.Delay(500);
  373. Assert.AreEqual(1, receivedMessages.Count);
  374. Assert.AreEqual("DA|18RS00SC00XI0000RV00R100R200R300R400L100L200L300L400Y100Y200AC0102031800BELK0000BM0000|", receivedMessages.First().ConvertPayloadToString());
  375. }
  376. }
  377. [TestMethod]
  378. public async Task Message_Send_Retry()
  379. {
  380. using (var testEnvironment = new TestEnvironment(TestContext))
  381. {
  382. testEnvironment.IgnoreClientLogErrors = true;
  383. testEnvironment.IgnoreServerLogErrors = true;
  384. await testEnvironment.StartServerAsync(
  385. new MqttServerOptionsBuilder()
  386. .WithPersistentSessions()
  387. .WithDefaultCommunicationTimeout(TimeSpan.FromMilliseconds(250)));
  388. var client1 = await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithCleanSession(false));
  389. await client1.SubscribeAsync("x", MqttQualityOfServiceLevel.AtLeastOnce);
  390. var retries = 0;
  391. async Task Handler1(MqttApplicationMessageReceivedEventArgs eventArgs)
  392. {
  393. retries++;
  394. await Task.Delay(1000);
  395. throw new Exception("Broken!");
  396. }
  397. client1.UseApplicationMessageReceivedHandler(Handler1);
  398. var client2 = await testEnvironment.ConnectClientAsync();
  399. await client2.PublishAsync("x");
  400. await Task.Delay(3000);
  401. // The server should disconnect clients which are not responding.
  402. Assert.IsFalse(client1.IsConnected);
  403. await client1.ReconnectAsync().ConfigureAwait(false);
  404. await Task.Delay(1000);
  405. Assert.AreEqual(2, retries);
  406. }
  407. }
  408. [TestMethod]
  409. public async Task NoConnectedHandler_Connect_DoesNotThrowException()
  410. {
  411. using (var testEnvironment = new TestEnvironment(TestContext))
  412. {
  413. await testEnvironment.StartServerAsync();
  414. var client = await testEnvironment.ConnectClientAsync();
  415. Assert.IsTrue(client.IsConnected);
  416. }
  417. }
  418. [TestMethod]
  419. public async Task NoDisconnectedHandler_Disconnect_DoesNotThrowException()
  420. {
  421. using (var testEnvironment = new TestEnvironment(TestContext))
  422. {
  423. await testEnvironment.StartServerAsync();
  424. var client = await testEnvironment.ConnectClientAsync();
  425. Assert.IsTrue(client.IsConnected);
  426. await client.DisconnectAsync();
  427. Assert.IsFalse(client.IsConnected);
  428. }
  429. }
  430. [TestMethod]
  431. public async Task Frequent_Connects()
  432. {
  433. using (var testEnvironment = new TestEnvironment(TestContext))
  434. {
  435. await testEnvironment.StartServerAsync();
  436. var clients = new List<IMqttClient>();
  437. for (var i = 0; i < 100; i++)
  438. {
  439. clients.Add(await testEnvironment.ConnectClientAsync(new MqttClientOptionsBuilder().WithClientId("a")));
  440. }
  441. var clientStatus = await testEnvironment.Server.GetClientStatusAsync();
  442. var sessionStatus = await testEnvironment.Server.GetSessionStatusAsync();
  443. for (var i = 0; i < 98; i++)
  444. {
  445. Assert.IsFalse(clients[i].IsConnected);
  446. }
  447. Assert.IsTrue(clients[99].IsConnected);
  448. Assert.AreEqual(1, clientStatus.Count);
  449. Assert.AreEqual(1, sessionStatus.Count);
  450. var receiveClient = clients[99];
  451. object receivedPayload = null;
  452. receiveClient.UseApplicationMessageReceivedHandler(e =>
  453. {
  454. receivedPayload = e.ApplicationMessage.ConvertPayloadToString();
  455. });
  456. await receiveClient.SubscribeAsync("x");
  457. var sendClient = await testEnvironment.ConnectClientAsync();
  458. await sendClient.PublishAsync("x", "1");
  459. await Task.Delay(100);
  460. Assert.AreEqual("1", receivedPayload);
  461. }
  462. }
  463. [TestMethod]
  464. public async Task No_Payload()
  465. {
  466. using (var testEnvironment = new TestEnvironment(TestContext))
  467. {
  468. await testEnvironment.StartServerAsync();
  469. var sender = await testEnvironment.ConnectClientAsync();
  470. var receiver = await testEnvironment.ConnectClientAsync();
  471. var message = new MqttApplicationMessageBuilder()
  472. .WithTopic("A");
  473. await receiver.SubscribeAsync(new MqttClientSubscribeOptions
  474. {
  475. TopicFilters = new List<TopicFilter> { new TopicFilter { Topic = "#" } }
  476. }, CancellationToken.None);
  477. MqttApplicationMessage receivedMessage = null;
  478. receiver.UseApplicationMessageReceivedHandler(e => receivedMessage = e.ApplicationMessage);
  479. await sender.PublishAsync(message.Build(), CancellationToken.None);
  480. await Task.Delay(1000);
  481. Assert.IsNotNull(receivedMessage);
  482. Assert.AreEqual("A", receivedMessage.Topic);
  483. Assert.AreEqual(null, receivedMessage.Payload);
  484. }
  485. }
  486. }
  487. }