Merge from develop.

This commit is contained in:
Christian Kratky
2018-11-16 22:16:21 +01:00
50 changed files with 845 additions and 168 deletions
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
@@ -7,9 +7,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.1" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
</ItemGroup>
<ItemGroup>
@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.IO.Pipelines;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
namespace MQTTnet.AspNetCore.Tests.Mockups
{
public class ConnectionContextMockup : ConnectionContext
{
public override string ConnectionId { get; set; }
public override IFeatureCollection Features { get; }
public override IDictionary<object, object> Items { get; set; }
public override IDuplexPipe Transport { get; set; }
public ConnectionContextMockup()
{
//Transport = new DefaultConnectionContext
}
}
}
@@ -0,0 +1,16 @@
using System.IO.Pipelines;
namespace MQTTnet.AspNetCore.Tests.Mockups
{
public class DuplexPipeMockup : IDuplexPipe
{
PipeReader IDuplexPipe.Input => Receive.Reader;
PipeWriter IDuplexPipe.Output => Send.Writer;
public Pipe Receive { get; set; } = new Pipe();
public Pipe Send { get; set; } = new Pipe();
}
}
@@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.AspNetCore.Tests.Mockups;
using MQTTnet.Exceptions;
using MQTTnet.Packets;
using MQTTnet.Serializer;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.AspNetCore.Tests
{
[TestClass]
public class MqttConnectionContextTest
{
[TestMethod]
public async Task TestReceivePacketAsyncThrowsWhenReaderCompleted()
{
var serializer = new MqttPacketSerializer {};
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
pipe.Receive.Writer.Complete();
await Assert.ThrowsExceptionAsync<MqttCommunicationException>(() => ctx.ReceivePacketAsync(TimeSpan.FromSeconds(1), CancellationToken.None));
}
[TestMethod]
public async Task TestParallelWrites()
{
var serializer = new MqttPacketSerializer { };
var pipe = new DuplexPipeMockup();
var connection = new DefaultConnectionContext();
connection.Transport = pipe;
var ctx = new MqttConnectionContext(serializer, connection);
var tasks = Enumerable.Range(1, 10).Select(_ => Task.Run(async () =>
{
for (int i = 0; i < 100; i++)
{
await ctx.SendPacketAsync(new MqttPublishPacket(), CancellationToken.None).ConfigureAwait(false);
}
}));
await Task.WhenAll(tasks).ConfigureAwait(false);
}
}
}
@@ -1,6 +1,4 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Diagnostics;
namespace MQTTnet.Benchmarks
@@ -8,9 +8,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.14" />
<PackageReference Include="System.IO.Pipelines" Version="4.5.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" />
<PackageReference Include="BenchmarkDotNet" Version="0.11.1" />
<PackageReference Include="System.IO.Pipelines" Version="4.5.2" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.4" />
</ItemGroup>
<ItemGroup>
@@ -1,7 +1,4 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Client;
using MQTTnet.Server;
@@ -2,8 +2,6 @@
using MQTTnet.Packets;
using MQTTnet.Serializer;
using MQTTnet.Internal;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Attributes.Exporters;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -1,6 +1,4 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using MQTTnet.Server;
using System;
@@ -0,0 +1,108 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Internal;
namespace MQTTnet.Core.Tests
{
[TestClass]
public class BlockingQueueTests
{
[TestMethod]
public void Preserve_Order()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual(3, queue.Count);
Assert.AreEqual("a", queue.Dequeue());
Assert.AreEqual("b", queue.Dequeue());
Assert.AreEqual("c", queue.Dequeue());
}
[TestMethod]
public void Remove_First_Items()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual("a", queue.RemoveFirst());
Assert.AreEqual("b", queue.RemoveFirst());
Assert.AreEqual(1, queue.Count);
Assert.AreEqual("c", queue.Dequeue());
}
[TestMethod]
public void Clear_Items()
{
var queue = new BlockingQueue<string>();
queue.Enqueue("a");
queue.Enqueue("b");
queue.Enqueue("c");
Assert.AreEqual(3, queue.Count);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
[TestMethod]
public async Task Wait_For_Item()
{
var queue = new BlockingQueue<string>();
string item = null;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() =>
{
item = queue.Dequeue();
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
await Task.Delay(100);
Assert.AreEqual(0, queue.Count);
Assert.AreEqual(null, item);
queue.Enqueue("x");
await Task.Delay(100);
Assert.AreEqual("x", item);
Assert.AreEqual(0, queue.Count);
}
[TestMethod]
public void Wait_For_Times()
{
var number = 0;
var queue = new BlockingQueue<int>();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(async () =>
{
while (true)
{
queue.Enqueue(1);
await Task.Delay(100);
}
});
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
while (number < 50)
{
queue.Dequeue();
Interlocked.Increment(ref number);
}
}
}
}
@@ -3,16 +3,17 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType>Full</DebugType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj" />
<ProjectReference Include="..\..\Source\MQTTnet\MQTTnet.csproj" />
</ItemGroup>
@@ -0,0 +1,38 @@
using MQTTnet.Extensions.ManagedClient;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Server;
namespace MQTTnet.Core.Tests
{
[TestClass]
public class ManagedMqttClientTests
{
[TestMethod]
public async Task Drop_New_Messages_On_Full_Queue()
{
var factory = new MqttFactory();
var managedClient = factory.CreateManagedMqttClient();
var clientOptions = new ManagedMqttClientOptionsBuilder()
.WithMaxPendingMessages(5)
.WithPendingMessagesOverflowStrategy(MqttPendingMessagesOverflowStrategy.DropNewMessage);
clientOptions.WithClientOptions(o => o.WithTcpServer("localhost"));
await managedClient.StartAsync(clientOptions.Build());
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "1" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "2" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "3" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "4" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "5" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "6" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "7" });
await managedClient.PublishAsync(new MqttApplicationMessage { Topic = "8" });
Assert.AreEqual(5, managedClient.PendingApplicationMessagesCount);
}
}
}
+137 -11
View File
@@ -5,9 +5,11 @@ using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Adapter;
using MQTTnet.Implementations;
namespace MQTTnet.Core.Tests
@@ -97,11 +99,11 @@ namespace MQTTnet.Core.Tests
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
var message = new MqttApplicationMessageBuilder().WithTopic("a").WithAtLeastOnceQoS().Build();
await c2.PublishAsync(message);
await Task.Delay(1000);
Assert.AreEqual(0, receivedMessagesCount);
var subscribeEventCalled = false;
s.ClientSubscribedTopic += (_, e) =>
{
@@ -115,7 +117,7 @@ namespace MQTTnet.Core.Tests
await c2.PublishAsync(message);
await Task.Delay(500);
Assert.AreEqual(1, receivedMessagesCount);
var unsubscribeEventCalled = false;
s.ClientUnsubscribedTopic += (_, e) =>
{
@@ -233,7 +235,7 @@ namespace MQTTnet.Core.Tests
await c1.PublishAsync(message);
}
}
[TestMethod]
public async Task MqttServer_ShutdownDisconnectsClientsGracefully()
{
@@ -302,6 +304,59 @@ namespace MQTTnet.Core.Tests
Assert.AreEqual(clientConnectedCalled, clientDisconnectedCalled);
}
[TestMethod]
public async Task MqttServer_LotsOfRetainedMessages()
{
const int ClientCount = 100;
var server = new MqttFactory().CreateMqttServer();
try
{
await server.StartAsync(new MqttServerOptionsBuilder().Build());
Parallel.For(
0,
ClientCount,
new ParallelOptions { MaxDegreeOfParallelism = 10 },
i =>
{
using (var client = new MqttFactory().CreateMqttClient())
{
client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build())
.GetAwaiter().GetResult();
for (var j = 0; j < 10; j++)
{
// Clear retained message.
client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i)
.WithPayload(new byte[0]).WithRetainFlag().Build()).GetAwaiter().GetResult();
// Set retained message.
client.PublishAsync(new MqttApplicationMessageBuilder().WithTopic("r" + i)
.WithPayload("value" + j).WithRetainFlag().Build()).GetAwaiter().GetResult();
}
client.DisconnectAsync().GetAwaiter().GetResult();
}
});
await Task.Delay(100);
var retainedMessages = server.GetRetainedMessages();
Assert.AreEqual(ClientCount, retainedMessages.Count);
for (var i = 0; i < ClientCount; i++)
{
Assert.IsTrue(retainedMessages.Any(m => m.Topic == "r" + i));
}
}
finally
{
await server.StopAsync();
}
}
[TestMethod]
public async Task MqttServer_RetainedMessagesFlow()
{
@@ -552,6 +607,51 @@ namespace MQTTnet.Core.Tests
Assert.IsTrue(bodyIsMatching);
}
[TestMethod]
public async Task MqttServer_ConnectionDenied()
{
var server = new MqttFactory().CreateMqttServer();
var client = new MqttFactory().CreateMqttClient();
try
{
var options = new MqttServerOptionsBuilder().WithConnectionValidator(context =>
{
context.ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
}).Build();
await server.StartAsync(options);
var clientOptions = new MqttClientOptionsBuilder()
.WithTcpServer("localhost").Build();
try
{
await client.ConnectAsync(clientOptions);
Assert.Fail("An exception should be raised.");
}
catch (Exception exception)
{
if (exception is MqttConnectingFailedException)
{
}
else
{
Assert.Fail("Wrong exception.");
}
}
}
finally
{
await client.DisconnectAsync();
await server.StopAsync();
client.Dispose();
}
}
[TestMethod]
public async Task MqttServer_SameClientIdConnectDisconnectEventOrder()
{
@@ -600,6 +700,33 @@ namespace MQTTnet.Core.Tests
Assert.IsFalse(connecteCalledBeforeConnectedClients, "ClientConnected was called before ClientDisconnect was called");
}
[TestMethod]
public async Task MqttServer_StopAndRestart()
{
var server = new MqttFactory().CreateMqttServer();
await server.StartAsync(new MqttServerOptions());
var client = new MqttFactory().CreateMqttClient();
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await server.StopAsync();
try
{
var client2 = new MqttFactory().CreateMqttClient();
await client2.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
Assert.Fail("Connecting should fail.");
}
catch (Exception)
{
}
await server.StartAsync(new MqttServerOptions());
var client3 = new MqttFactory().CreateMqttClient();
await client3.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
}
private class TestStorage : IMqttServerStorage
{
public IList<MqttApplicationMessage> Messages = new List<MqttApplicationMessage>();
@@ -623,25 +750,24 @@ namespace MQTTnet.Core.Tests
MqttQualityOfServiceLevel filterQualityOfServiceLevel,
int expectedReceivedMessagesCount)
{
var serverAdapter = new TestMqttServerAdapter();
var s = new MqttFactory().CreateMqttServer(new[] { serverAdapter }, new MqttNetLogger());
var s = new MqttFactory().CreateMqttServer();
var receivedMessagesCount = 0;
try
{
await s.StartAsync(new MqttServerOptions());
var c1 = await serverAdapter.ConnectTestClient("c1");
var c2 = await serverAdapter.ConnectTestClient("c2");
var c1 = new MqttFactory().CreateMqttClient();
c1.ApplicationMessageReceived += (_, __) => receivedMessagesCount++;
await c1.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await c1.SubscribeAsync(new TopicFilterBuilder().WithTopic(topicFilter).WithQualityOfServiceLevel(filterQualityOfServiceLevel).Build());
var c2 = new MqttFactory().CreateMqttClient();
await c2.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await c2.PublishAsync(builder => builder.WithTopic(topic).WithPayload(new byte[0]).WithQualityOfServiceLevel(qualityOfServiceLevel));
await Task.Delay(500);
await c1.UnsubscribeAsync(topicFilter);
await Task.Delay(500);
}
finally
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Client;
using MQTTnet.Server;
namespace MQTTnet.Core.Tests
{
[TestClass]
public class RoundtripTimeTests
{
[TestMethod]
public async Task Round_Trip_Time()
{
var factory = new MqttFactory();
var server = factory.CreateMqttServer();
var receiverClient = factory.CreateMqttClient();
var senderClient = factory.CreateMqttClient();
await server.StartAsync(new MqttServerOptionsBuilder().Build());
await receiverClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
await receiverClient.SubscribeAsync("#");
await senderClient.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost").Build());
TaskCompletionSource<string> response = null;
receiverClient.ApplicationMessageReceived += (sender, args) =>
{
response?.SetResult(args.ApplicationMessage.ConvertPayloadToString());
};
var times = new List<TimeSpan>();
var stopwatch = Stopwatch.StartNew();
for (var i = 0; i < 100; i++)
{
response = new TaskCompletionSource<string>();
await senderClient.PublishAsync("test", DateTime.UtcNow.Ticks.ToString());
response.Task.GetAwaiter().GetResult();
stopwatch.Stop();
times.Add(stopwatch.Elapsed);
stopwatch.Restart();
}
await server.StopAsync();
}
}
}
@@ -11,19 +11,20 @@ namespace MQTTnet.Core.Tests
/// publishes a message with a client and waits in the server until a message with the same topic is received
/// </summary>
/// <returns></returns>
public static async Task PublishAndWaitForAsync(this IMqttClient client, IMqttServer server, MqttApplicationMessage message)
public static async Task PublishAndWaitForAsync(this IMqttClient client, IMqttServer server, MqttApplicationMessage message)
{
var tcs = new TaskCompletionSource<object>();
EventHandler<MqttApplicationMessageReceivedEventArgs> handler = (sender, args) =>
void Handler(object sender, MqttApplicationMessageReceivedEventArgs args)
{
if (args.ApplicationMessage.Topic == message.Topic)
{
tcs.SetResult(true);
}
};
server.ApplicationMessageReceived += handler;
}
server.ApplicationMessageReceived += Handler;
try
{
await client.PublishAsync(message).ConfigureAwait(false);
@@ -31,7 +32,7 @@ namespace MQTTnet.Core.Tests
}
finally
{
server.ApplicationMessageReceived -= handler;
server.ApplicationMessageReceived -= Handler;
}
}
}
@@ -10,8 +10,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.4" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.5" />
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.1.1" />
</ItemGroup>
<ItemGroup>
@@ -3,11 +3,11 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<DebugType>Full</DebugType>
<TargetFrameworks>netcoreapp2.0;net452;net461</TargetFrameworks>
<TargetFrameworks>netcoreapp2.1;net452;net461</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.0|AnyCPU'">
<DefineConstants>RELEASE;NETCOREAPP2_0</DefineConstants>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netcoreapp2.1|AnyCPU'">
<DefineConstants>RELEASE;NETCOREAPP2_1</DefineConstants>
</PropertyGroup>
<ItemGroup>
@@ -142,10 +142,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.5</Version>
<Version>6.1.9</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls">
<Version>3.0.0</Version>
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">