-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
RemoteProtocolTests.cs
182 lines (158 loc) · 6.4 KB
/
RemoteProtocolTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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.Designer;
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();
if (t == typeof(Exception))
return new Exception("Here");
if (t == typeof(ExceptionDetails))
return new ExceptionDetails
{
ExceptionType = "Exception",
LineNumber = 5,
LinePosition = 6,
Message = "Here",
};
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; }
}
}