committed by
GitHub
5 changed files with 239 additions and 10 deletions
@ -1,6 +1,5 @@ |
|||
copy ..\samples\ControlCatalog.Desktop\bin\Debug\net461\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\net461\ |
|||
copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp2.0\ |
|||
copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard2.0\ |
|||
copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia.Gtk3.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard2.0\ |
|||
copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia.Win32.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard2.0\ |
|||
copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia.Skia.dll ~\.nuget\packages\avalonia.skia\$args\lib\netstandard2.0\ |
|||
copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp2.0\Avalonia.Skia.dll ~\.nuget\packages\avalonia.direct2d1\$args\lib\netstandard2.0\ |
|||
|
|||
@ -0,0 +1,62 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace Avalonia.DesignerSupport.Tests |
|||
{ |
|||
static class Helpers |
|||
{ |
|||
public static void StructDiff(object parsed, object expected) => StructDiff(parsed, expected, "{root}"); |
|||
|
|||
static void StructDiff(object parsed, object expected, string path) |
|||
{ |
|||
if (parsed == null && expected == null) |
|||
return; |
|||
if ((parsed == null && expected != null) || (parsed != null && expected == null)) |
|||
throw new Exception( |
|||
$"{path}: Null mismatch: {(parsed == null ? "null" : "not-null")} {(expected == null ? "null" : "not-null")}"); |
|||
|
|||
if (parsed.GetType() != expected.GetType()) |
|||
throw new Exception($"{path}: Type mismatch: {parsed.GetType()} {expected.GetType()}"); |
|||
|
|||
if (parsed is string || parsed.GetType().IsPrimitive) |
|||
{ |
|||
if (!parsed.Equals(expected)) |
|||
throw new Exception($"{path}: Not equal {parsed} {expected}"); |
|||
} |
|||
else if (parsed is IDictionary dic) |
|||
{ |
|||
var dic2 = (IDictionary) expected; |
|||
if (dic.Count != dic2.Count) |
|||
throw new Exception($"{path}: Dictionary count mismatch: {dic.Count} {dic2.Count}"); |
|||
|
|||
foreach (var k in dic.Keys.Cast<object>().OrderBy(o => o.ToString())) |
|||
{ |
|||
var v1 = dic[k]; |
|||
var v2 = dic2[k]; |
|||
StructDiff(v1, v2, path + "['" + k + "']"); |
|||
} |
|||
} |
|||
else if (parsed is IList col) |
|||
{ |
|||
var col2 = (IList) expected; |
|||
if (col.Count != col2.Count) |
|||
throw new Exception($"{path}: Collection count mismatch: {col.Count} {col2.Count}"); |
|||
for (var c = 0; c < col.Count; c++) |
|||
StructDiff(col[c], col2[c], path + "[" + c + "]"); |
|||
} |
|||
else |
|||
{ |
|||
foreach (var prop in parsed.GetType().GetProperties() |
|||
.Where(p => p.GetMethod != null && p.GetMethod.IsPublic)) |
|||
{ |
|||
StructDiff(prop.GetValue(parsed), prop.GetValue(expected), path + "." + prop.Name); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,171 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net; |
|||
using System.Net.Sockets; |
|||
using System.Reflection; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Remote.Protocol; |
|||
using Avalonia.Remote.Protocol.Viewport; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.DesignerSupport.Tests |
|||
{ |
|||
public class RemoteProtocolTests : IDisposable |
|||
{ |
|||
private readonly List<IDisposable> _disposables = new List<IDisposable>(); |
|||
private IAvaloniaRemoteTransportConnection _server; |
|||
private IAvaloniaRemoteTransportConnection _client; |
|||
private BlockingCollection<object> _serverMessages = new BlockingCollection<object>(); |
|||
private BlockingCollection<object> _clientMessages = new BlockingCollection<object>(); |
|||
private SynchronizationContext _originalContext; |
|||
|
|||
|
|||
class DisabledSyncContext : SynchronizationContext |
|||
{ |
|||
public override void Post(SendOrPostCallback d, object state) |
|||
{ |
|||
throw new InvalidCastException("Not allowed"); |
|||
} |
|||
|
|||
public override void Send(SendOrPostCallback d, object state) |
|||
{ |
|||
throw new InvalidCastException("Not allowed"); |
|||
} |
|||
} |
|||
|
|||
void Init(IMessageTypeResolver clientResolver = null, IMessageTypeResolver serverResolver = null) |
|||
{ |
|||
_originalContext = SynchronizationContext.Current; |
|||
SynchronizationContext.SetSynchronizationContext(new DisabledSyncContext()); |
|||
var clientTransport = new BsonTcpTransport(clientResolver ?? new DefaultMessageTypeResolver()); |
|||
var serverTransport = new BsonTcpTransport(serverResolver ?? new DefaultMessageTypeResolver()); |
|||
|
|||
var tcpListener = new TcpListener(IPAddress.Loopback, 0); |
|||
tcpListener.Start(); |
|||
var port = ((IPEndPoint)tcpListener.LocalEndpoint).Port; |
|||
tcpListener.Stop(); |
|||
|
|||
var tcs = new TaskCompletionSource<int>(); |
|||
serverTransport.Listen(IPAddress.Loopback, port, connected => |
|||
{ |
|||
_server = connected; |
|||
tcs.SetResult(0); |
|||
}); |
|||
_client = clientTransport.Connect(IPAddress.Loopback, port).Result; |
|||
_disposables.Add(_client); |
|||
_client.OnMessage += (_, m) => _clientMessages.Add(m); |
|||
tcs.Task.Wait(); |
|||
_disposables.Add(_server); |
|||
_server.OnMessage += (_, m) => _serverMessages.Add(m); |
|||
|
|||
} |
|||
|
|||
object TakeServer() |
|||
{ |
|||
var src = new CancellationTokenSource(200); |
|||
try |
|||
{ |
|||
return _serverMessages.Take(src.Token); |
|||
} |
|||
finally |
|||
{ |
|||
src.Dispose(); |
|||
} |
|||
|
|||
} |
|||
|
|||
[Fact] |
|||
void EntitiesAreProperlySerializedAndDeserialized() |
|||
{ |
|||
Init(); |
|||
var rnd = new Random(); |
|||
_server.OnMessage += (_, message) => { }; |
|||
|
|||
|
|||
object GetRandomValue(Type t, string pathInfo) |
|||
{ |
|||
if (t.IsArray) |
|||
{ |
|||
var arr = Array.CreateInstance(t.GetElementType(), 1); |
|||
((IList)arr)[0] = GetRandomValue(t.GetElementType(), pathInfo); |
|||
return arr; |
|||
} |
|||
|
|||
if (t == typeof(bool)) |
|||
return true; |
|||
if (t == typeof(int) || t == typeof(long)) |
|||
return rnd.Next(); |
|||
if (t == typeof(byte)) |
|||
return (byte)rnd.Next(255); |
|||
if (t == typeof(double)) |
|||
return rnd.NextDouble(); |
|||
if (t.IsEnum) |
|||
return ((IList)Enum.GetValues(t)).Cast<object>().Last(); |
|||
if (t == typeof(string)) |
|||
return Guid.NewGuid().ToString(); |
|||
if (t == typeof(Guid)) |
|||
return Guid.NewGuid(); |
|||
throw new Exception($"Doesn't know how to fabricate a random value for {t}, path {pathInfo}"); |
|||
} |
|||
|
|||
foreach (var t in typeof(MeasureViewportMessage).Assembly.GetTypes().Where(t => |
|||
t.GetCustomAttribute(typeof(AvaloniaRemoteMessageGuidAttribute)) != null)) |
|||
{ |
|||
var o = Activator.CreateInstance(t); |
|||
foreach (var p in t.GetProperties()) |
|||
p.SetValue(o, GetRandomValue(p.PropertyType, $"{t.FullName}.{p.Name}")); |
|||
|
|||
_client.Send(o).Wait(200); |
|||
var received = TakeServer(); |
|||
Helpers.StructDiff(received, o); |
|||
|
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
[Fact] |
|||
void RemoteProtocolShouldBeBackwardsCompatible() |
|||
{ |
|||
Init(new DefaultMessageTypeResolver(typeof(ExtendedMeasureViewportMessage).Assembly)); |
|||
_client.Send(new ExtendedMeasureViewportMessage() |
|||
{ |
|||
Width = 100, Height = 200, SomeNewProperty = 300, |
|||
SomeArrayProperty = new[]{1,2,3}, |
|||
SubObjectProperty = new ExtendedMeasureViewportMessage.SubObject() |
|||
{ |
|||
Foo = 543 |
|||
} |
|||
}); |
|||
var received = (MeasureViewportMessage)TakeServer(); |
|||
Assert.Equal(100, received.Width); |
|||
Assert.Equal(200, received.Height); |
|||
|
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_disposables.ForEach(d => d.Dispose()); |
|||
SynchronizationContext.SetSynchronizationContext(_originalContext); |
|||
} |
|||
} |
|||
|
|||
[AvaloniaRemoteMessageGuid("6E3C5310-E2B1-4C3D-8688-01183AA48C5B")] |
|||
public class ExtendedMeasureViewportMessage |
|||
{ |
|||
public double Width { get; set; } |
|||
|
|||
public int SomeNewProperty { get; set; } |
|||
public int[] SomeArrayProperty { get; set; } |
|||
public class SubObject |
|||
{ |
|||
public int Foo { get; set; } |
|||
} |
|||
public SubObject SubObjectProperty { get; set; } |
|||
public double Height { get; set; } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue