2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2026-06-13 14:38:43 +00:00
This commit is contained in:
2026-06-08 18:01:02 +03:00
parent 340798a5fa
commit 253a24a466
34 changed files with 237 additions and 1135 deletions
@@ -1,5 +1,5 @@
using Esiur.Data;
using RPC.EsiurTest;
using Esiur.Tests.RPC.EsiurServer;
using System;
using System.Buffers;
using System.Collections.Generic;
@@ -8,7 +8,7 @@ using System.Linq;
#nullable enable
namespace RPC.Client.Tests;
namespace Esiur.Tests.RPC.Client;
public static class DocGenerator
{
@@ -1,19 +1,9 @@
using Esiur.Net.Sockets;
using Esiur.Resource;
using RPC.EsiurTest;
using System;
using System.Collections.Generic;
using Esiur.Tests.RPC.EsiurServer;
using System.Diagnostics;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Esiur.Misc;
using Esiur.Data;
namespace RPC.Client.Tests.Docs
namespace Esiur.Tests.RPC.Client
{
public static class EsiurTest
{
@@ -1,6 +1,6 @@
using Echo.Model.Grpc;
using Esiur.Net.Sockets;
using Esiur.Net.Sockets;
using Esiur.Resource;
using Esiur.Tests.RPC.Client.Grpc;
using Google.Protobuf;
using Grpc.Net.Client;
using System;
@@ -8,7 +8,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace RPC.Client.Tests.Docs;
namespace Esiur.Tests.RPC.Client;
public class GrpcTest
{
@@ -26,7 +26,7 @@ public class GrpcTest
Console.WriteLine($"\n== Grpc @ {address} ==");
using var channel = GrpcChannel.ForAddress(address);
var service = new Echo.Model.Grpc.EchoService.EchoServiceClient(channel);
var service = new Client.Grpc.EchoService.EchoServiceClient(channel);
Thread.Sleep(3000);
@@ -1,4 +1,4 @@
using RPC.EsiurTest;
using Esiur.Tests.RPC.EsiurServer;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -6,7 +6,7 @@ using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace RPC.Client.Tests.Docs;
namespace Esiur.Tests.RPC.Client;
public class JsonTest
@@ -1,10 +0,0 @@
using System;
namespace Esiur
{
public static class Generated
{
public static Type[] Resources { get; } = new Type[] { typeof(RPC.EsiurTest.Service), typeof(RPC.EsiurTest.TestObject) };
public static Type[] Records { get; } = new Type[] { typeof(RPC.EsiurTest.BusinessDocument), typeof(RPC.EsiurTest.Attachment), typeof(RPC.EsiurTest.Party), typeof(RPC.EsiurTest.Address), typeof(RPC.EsiurTest.DocumentHeader), typeof(RPC.EsiurTest.LineItem), typeof(RPC.EsiurTest.Variant), typeof(RPC.EsiurTest.Payment) };
public static Type[] Enums { get; } = new Type[] { typeof(RPC.EsiurTest.Currency), typeof(RPC.EsiurTest.DocType), typeof(RPC.EsiurTest.Kind), typeof(RPC.EsiurTest.LineType), typeof(RPC.EsiurTest.PaymentMethod) };
}
}
@@ -3,28 +3,29 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("0f8f447ee993847189b2c1ad6f83931a")]
[Remote("Esiur.Tests.RPC.EsiurServer.Address", "")]
[Export]
public class Address : IRecord
{
[Annotation("String")]
[Annotation("", "String")]
public string City { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Country { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Line1 { get; set; }
[Annotation("String")]
public string? Line2 { get; set; }
[Annotation("", "String")]
public string Line2 { get; set; }
[Annotation("String")]
public string? PostalCode { get; set; }
[Annotation("", "String")]
public string PostalCode { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Region { get; set; }
public override bool Equals(object? obj)
@@ -41,9 +42,9 @@ namespace RPC.EsiurTest
return true;
}
public SharedModel.Address ToShared()
public Client.SharedModel.Address ToShared()
{
return new SharedModel.Address()
return new Client.SharedModel.Address()
{
City = City,
Country = Country,
@@ -55,9 +56,9 @@ namespace RPC.EsiurTest
};
}
public Echo.Model.Grpc.Address ToGrpc()
public Esiur.Tests.RPC.Client.Grpc.Address ToGrpc()
{
return new Echo.Model.Grpc.Address()
return new Esiur.Tests.RPC.Client.Grpc.Address()
{
City = City,
Country = Country,
@@ -80,5 +81,6 @@ namespace RPC.EsiurTest
Region = Region,
};
}
}
}
@@ -1,24 +1,25 @@
using System;
using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Esiur.Resource;
using Google.Protobuf;
namespace RPC.EsiurTest
using System;
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("4befaa686f038a2885268fca4cbf3c2c")]
[Remote("Esiur.Tests.RPC.EsiurServer.Attachment", "")]
[Export]
public class Attachment : IRecord
{
[Annotation("Byte[]")]
[Annotation("", "Byte[]")]
public byte[] Data { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string MimeType { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Name { get; set; }
public override bool Equals(object? obj)
{
var other = obj as Attachment;
@@ -29,9 +30,9 @@ namespace RPC.EsiurTest
return true;
}
public SharedModel.Attachment ToShared()
public Client.SharedModel.Attachment ToShared()
{
return new SharedModel.Attachment()
return new Client.SharedModel.Attachment()
{
Data = Data,
MimeType = MimeType,
@@ -39,9 +40,9 @@ namespace RPC.EsiurTest
};
}
public Echo.Model.Grpc.Attachment ToGrpc()
public Esiur.Tests.RPC.Client.Grpc.Attachment ToGrpc()
{
return new Echo.Model.Grpc.Attachment()
return new Esiur.Tests.RPC.Client.Grpc.Attachment()
{
Data = ByteString.CopyFrom(Data),
MimeType = MimeType,
@@ -3,34 +3,32 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Google.Protobuf;
using Google.Protobuf.Collections;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("9a34d22890e787b48133a2a61ac84ad8")]
[Remote("Esiur.Tests.RPC.EsiurServer.BusinessDocument", "")]
[Export]
public class BusinessDocument : IRecord
{
[Annotation("Attachment[]")]
public RPC.EsiurTest.Attachment[] Attachments { get; set; }
[Annotation("", "Attachment[]")]
public Esiur.Tests.RPC.EsiurServer.Attachment[] Attachments { get; set; }
[Annotation("Party")]
public RPC.EsiurTest.Party Buyer { get; set; }
[Annotation("", "Party")]
public Esiur.Tests.RPC.EsiurServer.Party Buyer { get; set; }
[Annotation("DocumentHeader")]
public RPC.EsiurTest.DocumentHeader Header { get; set; }
[Annotation("", "DocumentHeader")]
public Esiur.Tests.RPC.EsiurServer.DocumentHeader Header { get; set; }
[Annotation("LineItem[]")]
public RPC.EsiurTest.LineItem[] Items { get; set; }
[Annotation("", "LineItem[]")]
public Esiur.Tests.RPC.EsiurServer.LineItem[] Items { get; set; }
[Annotation("Payment[]")]
public RPC.EsiurTest.Payment[] Payments { get; set; }
[Annotation("", "Payment[]")]
public Esiur.Tests.RPC.EsiurServer.Payment[] Payments { get; set; }
[Annotation("Int32[]")]
[Annotation("", "Int32[]")]
public int[] RiskScores { get; set; }
[Annotation("Party")]
public RPC.EsiurTest.Party Seller { get; set; }
[Annotation("", "Party")]
public Esiur.Tests.RPC.EsiurServer.Party Seller { get; set; }
public override bool Equals(object? obj)
{
@@ -68,15 +66,15 @@ namespace RPC.EsiurTest
}
public SharedModel.BusinessDocument ToShared()
public Client.SharedModel.BusinessDocument ToShared()
{
return new SharedModel.BusinessDocument()
return new Client.SharedModel.BusinessDocument()
{
Attachments = Attachments?.Select(x=>x.ToShared()).ToArray() ?? null,
Attachments = Attachments?.Select(x => x.ToShared()).ToArray() ?? null,
Buyer = Buyer.ToShared(),
Header = Header.ToShared(),
Items = Items.Select(x=>x.ToShared()).ToArray(),
Payments = Payments.Select(x=>x.ToShared()).ToArray(),
Items = Items.Select(x => x.ToShared()).ToArray(),
Payments = Payments.Select(x => x.ToShared()).ToArray(),
RiskScores = RiskScores,
Seller = Seller.ToShared(),
};
@@ -96,7 +94,7 @@ namespace RPC.EsiurTest
rt.Seller = Seller.ToThrift();
if (Attachments != null)
rt.Attachments = Attachments.Select(x=>x.ToThrift()).ToList();
rt.Attachments = Attachments.Select(x => x.ToThrift()).ToList();
if (RiskScores != null)
rt.RiskScores = RiskScores.ToList();
@@ -110,10 +108,10 @@ namespace RPC.EsiurTest
return rt;
}
public Echo.Model.Grpc.BusinessDocument ToGrpc()
public Esiur.Tests.RPC.Client.Grpc.BusinessDocument ToGrpc()
{
var rt = new Echo.Model.Grpc.BusinessDocument()
var rt = new Esiur.Tests.RPC.Client.Grpc.BusinessDocument()
{
Header = Header.ToGrpc(),
Buyer = Buyer.ToGrpc(),
@@ -140,6 +138,5 @@ namespace RPC.EsiurTest
return rt;
}
}
}
@@ -3,9 +3,9 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("c44e42333dfd8d3485bb2a79fd7a9f6f")]
[Remote("Esiur.Tests.RPC.EsiurServer.Currency", "")]
[Export]
public enum Currency
{
@@ -3,9 +3,9 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("6ded4eca74c8886a85a74e082770be4b")]
[Remote("Esiur.Tests.RPC.EsiurServer.DocType", "")]
[Export]
public enum DocType
{
@@ -4,49 +4,49 @@ using Esiur.Protocol;
using Esiur.Resource;
using Google.Protobuf;
using System;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("4631164f62d489e68ffab70e20b421f2")]
[Remote("Esiur.Tests.RPC.EsiurServer.DocumentHeader", "")]
[Export]
public class DocumentHeader : IRecord
{
[Annotation("DateTime")]
[Annotation("", "DateTime")]
public DateTime CreatedAt { get; set; }
[Annotation("Currency")]
public RPC.EsiurTest.Currency Currency { get; set; }
[Annotation("", "Currency")]
public Esiur.Tests.RPC.EsiurServer.Currency Currency { get; set; }
[Annotation("Byte[]")]
[Annotation("", "Byte[]")]
public byte[] DocId { get; set; }
[Annotation("Dictionary`2")]
public Map<string, RPC.EsiurTest.Variant> Meta { get; set; }
[Annotation("", "Dictionary`2")]
public Map<string, Esiur.Tests.RPC.EsiurServer.Variant> Meta { get; set; }
[Annotation("String")]
public string? Notes { get; set; }
[Annotation("", "String")]
public string Notes { get; set; }
[Annotation("DocType")]
public RPC.EsiurTest.DocType Type { get; set; }
[Annotation("", "DocType")]
public Esiur.Tests.RPC.EsiurServer.DocType Type { get; set; }
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public DateTime? UpdatedAt { get; set; }
[Annotation("Int32")]
[Annotation("", "Int32")]
public int Version { get; set; }
public SharedModel.DocumentHeader ToShared()
public Client.SharedModel.DocumentHeader ToShared()
{
return new SharedModel.DocumentHeader()
return new Client.SharedModel.DocumentHeader()
{
CreatedAt = CreatedAt,
DocId = DocId,
Meta = Meta.ToDictionary(x=>x.Key, v=>v.Value.ToShared()),
Meta = Meta.ToDictionary(x => x.Key, v => v.Value.ToShared()),
Notes = Notes,
Currency = Enum.Parse<SharedModel.Currency>(Currency.ToString(), true),
Currency = Enum.Parse<Client.SharedModel.Currency>(Currency.ToString(), true),
UpdatedAt = UpdatedAt,
Version = Version,
Type = Enum.Parse<SharedModel.DocType>(Type.ToString(), true)
Type = Enum.Parse<Client.SharedModel.DocType>(Type.ToString(), true)
};
}
@@ -56,10 +56,10 @@ namespace RPC.EsiurTest
{
DocId = DocId,
CreatedAt = CreatedAt.Ticks,
Currency = Enum.Parse< Echo.ThriftModel.Currency>(Currency.ToString(), true),
Type = Enum.Parse< Echo.ThriftModel.DocType>(Type.ToString(), true),
Currency = Enum.Parse<Echo.ThriftModel.Currency>(Currency.ToString(), true),
Type = Enum.Parse<Echo.ThriftModel.DocType>(Type.ToString(), true),
Version = Version,
Meta = Meta.ToDictionary(x=>x.Key, x=>x.Value.ToThrift())
Meta = Meta.ToDictionary(x => x.Key, x => x.Value.ToThrift())
};
if (UpdatedAt != null)
@@ -71,13 +71,13 @@ namespace RPC.EsiurTest
return rt;
}
public Echo.Model.Grpc.DocumentHeader ToGrpc()
public Esiur.Tests.RPC.Client.Grpc.DocumentHeader ToGrpc()
{
var hdr = new Echo.Model.Grpc.DocumentHeader();
var hdr = new Esiur.Tests.RPC.Client.Grpc.DocumentHeader();
hdr.DocId = ByteString.CopyFrom(DocId);
hdr.CreatedAt = CreatedAt.Ticks;
hdr.Currency = Enum.Parse<Echo.Model.Grpc.Currency>(Currency.ToString(), true);
hdr.Currency = Enum.Parse<Esiur.Tests.RPC.Client.Grpc.Currency>(Currency.ToString(), true);
hdr.Version = Version;
hdr.Notes = Notes;
@@ -3,9 +3,9 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("32ae8265068382608399b7e427be37db")]
[Remote("Esiur.Tests.RPC.EsiurServer.Kind", "")]
[Export]
public enum Kind
{
@@ -3,46 +3,46 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("142f42b0e1a78c098f35fa935cde22c1")]
[Remote("Esiur.Tests.RPC.EsiurServer.LineItem", "")]
[Export]
public class LineItem : IRecord
{
[Annotation("String")]
[Annotation("", "String")]
public string Description { get; set; }
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public double? Discount { get; set; }
[Annotation("Map`2")]
public Map<string, RPC.EsiurTest.Variant> Ext { get; set; }
[Annotation("", "Map`2")]
public Map<string, Esiur.Tests.RPC.EsiurServer.Variant> Ext { get; set; }
[Annotation("Int32")]
[Annotation("", "Int32")]
public int LineNo { get; set; }
[Annotation("Double")]
[Annotation("", "Double")]
public double Qty { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string QtyUnit { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string SKU { get; set; }
[Annotation("LineType")]
public RPC.EsiurTest.LineType Type { get; set; }
[Annotation("", "LineType")]
public Esiur.Tests.RPC.EsiurServer.LineType Type { get; set; }
[Annotation("Double")]
[Annotation("", "Double")]
public double UnitPrice { get; set; }
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public double? VatRate { get; set; }
public SharedModel.LineItem ToShared()
public Client.SharedModel.LineItem ToShared()
{
return new SharedModel.LineItem()
return new Client.SharedModel.LineItem()
{
Description = Description,
Discount = Discount,
@@ -51,7 +51,7 @@ namespace RPC.EsiurTest
Qty = Qty,
QtyUnit = QtyUnit,
SKU = SKU,
Type = Enum.Parse<SharedModel.LineType>(Type.ToString(), true),
Type = Enum.Parse<Client.SharedModel.LineType>(Type.ToString(), true),
UnitPrice = UnitPrice,
VatRate = VatRate
};
@@ -80,9 +80,9 @@ namespace RPC.EsiurTest
return rt;
}
public Echo.Model.Grpc.LineItem ToGrpc()
public Esiur.Tests.RPC.Client.Grpc.LineItem ToGrpc()
{
var rt = new Echo.Model.Grpc.LineItem()
var rt = new Esiur.Tests.RPC.Client.Grpc.LineItem()
{
Description = Description,
Discount = Discount ?? 0,
@@ -91,7 +91,7 @@ namespace RPC.EsiurTest
UnitPrice = UnitPrice,
QtyUnit = QtyUnit,
Sku = SKU,
Type = Enum.Parse<Echo.Model.Grpc.LineType>(Type.ToString(), true),
Type = Enum.Parse<Esiur.Tests.RPC.Client.Grpc.LineType>(Type.ToString(), true),
VatRate = VatRate ?? 0,
};
@@ -3,9 +3,9 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("7e474e8826e288f28bddddf69782c580")]
[Remote("Esiur.Tests.RPC.EsiurServer.LineType", "")]
[Export]
public enum LineType
{
@@ -3,36 +3,36 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("44fff9c7bd9b86f580bf479a64cb84af")]
[Remote("Esiur.Tests.RPC.EsiurServer.Party", "")]
[Export]
public class Party : IRecord
{
[Annotation("Address")]
public RPC.EsiurTest.Address Address { get; set; }
[Annotation("", "Address")]
public Esiur.Tests.RPC.EsiurServer.Address Address { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Email { get; set; }
[Annotation("UInt64")]
[Annotation("", "UInt64")]
public ulong Id { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Name { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Phone { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string PreferredLanguage { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string TaxId { get; set; }
public SharedModel.Party ToShared()
public Client.SharedModel.Party ToShared()
{
return new SharedModel.Party()
return new Client.SharedModel.Party()
{
Address = Address.ToShared(),
Email = Email,
@@ -58,9 +58,9 @@ namespace RPC.EsiurTest
};
}
public Echo.Model.Grpc.Party ToGrpc()
public Esiur.Tests.RPC.Client.Grpc.Party ToGrpc()
{
return new Echo.Model.Grpc.Party()
return new Esiur.Tests.RPC.Client.Grpc.Party()
{
Address = Address.ToGrpc(),
Email = Email,
@@ -3,38 +3,38 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("f172196340298b8586fed434c72bc158")]
[Remote("Esiur.Tests.RPC.EsiurServer.Payment", "")]
[Export]
public class Payment : IRecord
{
[Annotation("Double")]
[Annotation("", "Double")]
public double Amount { get; set; }
[Annotation("Currency")]
public RPC.EsiurTest.Currency Currency { get; set; }
[Annotation("", "Currency")]
public Esiur.Tests.RPC.EsiurServer.Currency Currency { get; set; }
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public double? Fee { get; set; }
[Annotation("PaymentMethod")]
public RPC.EsiurTest.PaymentMethod Method { get; set; }
[Annotation("", "PaymentMethod")]
public Esiur.Tests.RPC.EsiurServer.PaymentMethod Method { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Reference { get; set; }
[Annotation("DateTime")]
[Annotation("", "DateTime")]
public DateTime Timestamp { get; set; }
public SharedModel.Payment ToShared()
public Client.SharedModel.Payment ToShared()
{
return new SharedModel.Payment()
return new Client.SharedModel.Payment()
{
Amount = Amount,
Currency = Enum.Parse<SharedModel.Currency>(Currency.ToString(), true),
Method = Enum.Parse<SharedModel.PaymentMethod>(Method.ToString(), true),
Currency = Enum.Parse<Client.SharedModel.Currency>(Currency.ToString(), true),
Method = Enum.Parse<Client.SharedModel.PaymentMethod>(Method.ToString(), true),
Reference = Reference,
Timestamp = Timestamp,
Fee = Fee,
@@ -43,7 +43,7 @@ namespace RPC.EsiurTest
public Echo.ThriftModel.Payment ToThrift()
{
var rt= new Echo.ThriftModel.Payment()
var rt = new Echo.ThriftModel.Payment()
{
Amount = Amount,
Currency = Enum.Parse<Echo.ThriftModel.Currency>(Currency.ToString(), true),
@@ -58,14 +58,14 @@ namespace RPC.EsiurTest
return rt;
}
public Echo.Model.Grpc.Payment ToGrpc()
public Esiur.Tests.RPC.Client.Grpc.Payment ToGrpc()
{
return new Echo.Model.Grpc.Payment()
return new Esiur.Tests.RPC.Client.Grpc.Payment()
{
Amount = Amount,
Currency = Enum.Parse<Echo.Model.Grpc.Currency>(Currency.ToString(), true),
Currency = Enum.Parse<Esiur.Tests.RPC.Client.Grpc.Currency>(Currency.ToString(), true),
Fee = Fee ?? 0,
Method = Enum.Parse<Echo.Model.Grpc.PaymentMethod>(Method.ToString(), true),
Method = Enum.Parse<Esiur.Tests.RPC.Client.Grpc.PaymentMethod>(Method.ToString(), true),
Reference = Reference,
Timestamp = Timestamp.Ticks,
};
@@ -3,9 +3,9 @@ using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("fadfe3764f808d7e839fef5275490dd7")]
[Remote("Esiur.Tests.RPC.EsiurServer.PaymentMethod", "")]
[Export]
public enum PaymentMethod
{
@@ -14,6 +14,5 @@ namespace RPC.EsiurTest
Crypto = 3,
Other = 4,
Wire = 2
}
}
@@ -4,15 +4,14 @@ using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
#nullable enable
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
//{ab8e681b-61d9-8fb7-8c63-bbded15457e1}
[TypeId("f7e00be8881d88d68a8a2dc2d3a4b3d1")]
[Remote("Esiur.Tests.RPC.EsiurServer.Service", "")]
public class Service : EpResource
{
public Service(EpConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) { }
public Service() { }
[Annotation("([Int32] count,[Int32] size,[Int32] delay) -> AsyncReply`1")]
[Annotation("", "([Int32] count,[Int32] size,[Int32] delay) -> AsyncReply`1")]
[Export]
public AsyncReply<byte[]> ChunkTest(int count, int size, int delay)
{
@@ -24,7 +23,7 @@ namespace RPC.EsiurTest
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Byte[]] payload) -> Byte[]")]
[Annotation("", "([Byte[]] payload) -> Byte[]")]
[Export]
public AsyncReply<byte[]> EchoBytes(byte[] payload)
{
@@ -36,31 +35,31 @@ namespace RPC.EsiurTest
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([BusinessDocument[]] payload) -> BusinessDocument[]")]
[Annotation("", "([BusinessDocument[]] payload) -> BusinessDocument[]")]
[Export]
public AsyncReply<RPC.EsiurTest.BusinessDocument[]> EchoDocuments(RPC.EsiurTest.BusinessDocument[] payload)
public AsyncReply<Esiur.Tests.RPC.EsiurServer.BusinessDocument[]> EchoDocuments(Esiur.Tests.RPC.EsiurServer.BusinessDocument[] payload)
{
var args = new Map<byte, object>() { [0] = payload };
var rt = new AsyncReply<RPC.EsiurTest.BusinessDocument[]>();
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.BusinessDocument[]>();
_Invoke(2, args)
.Then(x => rt.Trigger((RPC.EsiurTest.BusinessDocument[])x))
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.BusinessDocument[])x))
.Error(x => rt.TriggerError(x))
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([DocType[]] payload) -> DocType[]")]
[Annotation("", "([DocType[]] payload) -> DocType[]")]
[Export]
public AsyncReply<RPC.EsiurTest.DocType[]> EchoEnumArray(RPC.EsiurTest.DocType[] payload)
public AsyncReply<Esiur.Tests.RPC.EsiurServer.DocType[]> EchoEnumArray(Esiur.Tests.RPC.EsiurServer.DocType[] payload)
{
var args = new Map<byte, object>() { [0] = payload };
var rt = new AsyncReply<RPC.EsiurTest.DocType[]>();
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.DocType[]>();
_Invoke(3, args)
.Then(x => rt.Trigger((RPC.EsiurTest.DocType[])x))
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.DocType[])x))
.Error(x => rt.TriggerError(x))
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Int32[]] payload) -> Int32[]")]
[Annotation("", "([Int32[]] payload) -> Int32[]")]
[Export]
public AsyncReply<int[]> EchoIntArray(int[] payload)
{
@@ -72,19 +71,19 @@ namespace RPC.EsiurTest
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Map`2] payload) -> Map`2")]
[Annotation("", "([Map`2] payload) -> Map`2")]
[Export]
public AsyncReply<Map<string, RPC.EsiurTest.BusinessDocument>> EchoMap(Map<string, RPC.EsiurTest.BusinessDocument> payload)
public AsyncReply<Map<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument>> EchoMap(Map<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument> payload)
{
var args = new Map<byte, object>() { [0] = payload };
var rt = new AsyncReply<Map<string, RPC.EsiurTest.BusinessDocument>>();
var rt = new AsyncReply<Map<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument>>();
_Invoke(5, args)
.Then(x => rt.Trigger((Map<string, RPC.EsiurTest.BusinessDocument>)x))
.Then(x => rt.Trigger((Map<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument>)x))
.Error(x => rt.TriggerError(x))
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([String[]] payload) -> String[]")]
[Annotation("", "([String[]] payload) -> String[]")]
[Export]
public AsyncReply<string[]> EchoStringArray(string[] payload)
{
@@ -96,7 +95,7 @@ namespace RPC.EsiurTest
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Int32] count,[Int32] size,[Int32] delay) -> Void")]
[Annotation("", "([Int32] count,[Int32] size,[Int32] delay) -> Void")]
[Export]
public AsyncReply<object> EventTest(int count, int size, int delay)
{
@@ -108,7 +107,7 @@ namespace RPC.EsiurTest
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Int32] count,[Int32] size,[Int32] delay) -> Void")]
[Annotation("", "([Int32] count,[Int32] size,[Int32] delay) -> Void")]
[Export]
public AsyncReply<object> PropertyChangeTest(int count, int size, int delay)
{
@@ -120,62 +119,62 @@ namespace RPC.EsiurTest
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Int32] interval,[Int32] count,[Double] localProbability,[Double] remoteProbability,[String] remoteHostLink) -> AsyncReply`1")]
[Annotation("", "([Int32] interval,[Int32] count,[Double] localProbability,[Double] remoteProbability,[String] remoteHostLink) -> AsyncReply`1")]
[Export]
public AsyncReply<RPC.EsiurTest.TestObject> StartUpdates(int interval, int count, double localProbability, double remoteProbability, string remoteHostLink)
public AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject> StartUpdates(int interval, int count, double localProbability, double remoteProbability, string remoteHostLink)
{
var args = new Map<byte, object>() { [0] = interval, [1] = count, [2] = localProbability, [3] = remoteProbability, [4] = remoteHostLink };
var rt = new AsyncReply<RPC.EsiurTest.TestObject>();
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject>();
_Invoke(9, args)
.Then(x => rt.Trigger((RPC.EsiurTest.TestObject)x))
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.TestObject)x))
.Error(x => rt.TriggerError(x))
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Int32] interval,[Int32] count,[Double] localProbability) -> AsyncReply`1")]
[Annotation("", "([Int32] interval,[Int32] count,[Double] localProbability) -> AsyncReply`1")]
[Export]
public AsyncReply<RPC.EsiurTest.TestObject> StartUpdatesLocal(int interval, int count, double localProbability)
public AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject> StartUpdatesLocal(int interval, int count, double localProbability)
{
var args = new Map<byte, object>() { [0] = interval, [1] = count, [2] = localProbability };
var rt = new AsyncReply<RPC.EsiurTest.TestObject>();
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject>();
_Invoke(10, args)
.Then(x => rt.Trigger((RPC.EsiurTest.TestObject)x))
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.TestObject)x))
.Error(x => rt.TriggerError(x))
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Int32] interval,[Int32] count,[Double] remoteProbability,[String] remoteNode,[String] remoteLink) -> AsyncReply`1")]
[Annotation("", "([Int32] interval,[Int32] count,[Double] remoteProbability,[String] remoteNode,[String] remoteLink) -> AsyncReply`1")]
[Export]
public AsyncReply<RPC.EsiurTest.TestObject> StartUpdatesMirror(int interval, int count, double remoteProbability, string remoteNode, string remoteLink)
public AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject> StartUpdatesMirror(int interval, int count, double remoteProbability, string remoteNode, string remoteLink)
{
var args = new Map<byte, object>() { [0] = interval, [1] = count, [2] = remoteProbability, [3] = remoteNode, [4] = remoteLink };
var rt = new AsyncReply<RPC.EsiurTest.TestObject>();
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject>();
_Invoke(11, args)
.Then(x => rt.Trigger((RPC.EsiurTest.TestObject)x))
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.TestObject)x))
.Error(x => rt.TriggerError(x))
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("([Int32] interval,[Int32] count,[Double] remoteProbability,[String] remoteLink) -> AsyncReply`1")]
[Annotation("", "([Int32] interval,[Int32] count,[Double] remoteProbability,[String] remoteLink) -> AsyncReply`1")]
[Export]
public AsyncReply<RPC.EsiurTest.TestObject> StartUpdatesRemote(int interval, int count, double remoteProbability, string remoteLink)
public AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject> StartUpdatesRemote(int interval, int count, double remoteProbability, string remoteLink)
{
var args = new Map<byte, object>() { [0] = interval, [1] = count, [2] = remoteProbability, [3] = remoteLink };
var rt = new AsyncReply<RPC.EsiurTest.TestObject>();
var rt = new AsyncReply<Esiur.Tests.RPC.EsiurServer.TestObject>();
_Invoke(12, args)
.Then(x => rt.Trigger((RPC.EsiurTest.TestObject)x))
.Then(x => rt.Trigger((Esiur.Tests.RPC.EsiurServer.TestObject)x))
.Error(x => rt.TriggerError(x))
.Chunk(x => rt.TriggerChunk(x));
return rt;
}
[Annotation("Byte[]")]
[Annotation("", "Byte[]")]
[Export]
public byte[] MessageToChange
{
get => (byte[])_properties[0];
set => SetResourceProperty(0, value);
}
[Annotation("Object")]
[Annotation("", "Object")]
[Export]
public object TestProperty
{
@@ -4,32 +4,32 @@ using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
#nullable enable
namespace RPC.EsiurTest
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("d90d3558e2b18d9a8f45707372ddf2c3")]
[Remote("Esiur.Tests.RPC.EsiurServer.TestObject", "")]
public class TestObject : EpResource
{
public TestObject(EpConnection connection, uint instanceId, ulong age, string link) : base(connection, instanceId, age, link) { }
public TestObject() { }
[Annotation("String")]
[Annotation("", "String")]
[Export]
public string Name
{
get => (string)properties[0];
get => (string)_properties[0];
set => SetResourceProperty(0, value);
}
[Annotation("Int32")]
[Annotation("", "Int32")]
[Export]
public int Size
{
get => (int)properties[1];
get => (int)_properties[1];
set => SetResourceProperty(1, value);
}
[Annotation("Object")]
[Annotation("", "Object")]
[Export]
public object Value
{
get => (object)properties[2];
get => (object)_properties[2];
set => SetResourceProperty(2, value);
}
@@ -1,46 +1,47 @@
using System;
using Esiur.Resource;
using Esiur.Core;
using Esiur.Data;
using Esiur.Protocol;
using Esiur.Resource;
using Google.Protobuf;
namespace RPC.EsiurTest
using System;
namespace Esiur.Tests.RPC.EsiurServer
{
[TypeId("91ed22c5c53e846181f799dc76ddd93c")]
[Remote("Esiur.Tests.RPC.EsiurServer.Variant", "")]
[Export]
public class Variant : IRecord
{
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public bool? Bool { get; set; }
[Annotation("Byte[]")]
[Annotation("", "Byte[]")]
public byte[] Bytes { get; set; }
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public DateTime? Dt { get; set; }
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public double? F64 { get; set; }
[Annotation("Byte[]")]
[Annotation("", "Byte[]")]
public byte[] Guid { get; set; }
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public long? I64 { get; set; }
[Annotation("String")]
[Annotation("", "String")]
public string Str { get; set; }
[Annotation("Kind")]
public RPC.EsiurTest.Kind Tag { get; set; }
[Annotation("", "Kind")]
public Esiur.Tests.RPC.EsiurServer.Kind Tag { get; set; }
[Annotation("Nullable`1?")]
[Annotation("", "Nullable`1?")]
public ulong? U64 { get; set; }
public RPC.SharedModel.Variant ToShared()
public Client.SharedModel.Variant ToShared()
{
return new SharedModel.Variant()
return new Client.SharedModel.Variant()
{
Bool = Bool,
@@ -50,14 +51,14 @@ namespace RPC.EsiurTest
Guid = Guid,
I64 = I64,
Str = Str,
Tag = Enum.Parse<SharedModel.Kind>(Tag.ToString(), true),
Tag = Enum.Parse<Client.SharedModel.Kind>(Tag.ToString(), true),
U64 = U64
};
}
public Echo.Model.Grpc.Variant ToGrpc()
public Client.Grpc.Variant ToGrpc()
{
return new Echo.Model.Grpc.Variant()
return new Client.Grpc.Variant()
{
BoolVal = Bool ?? false,
BytesVal = ByteString.CopyFrom(Bytes ?? new byte[0]),
@@ -66,7 +67,7 @@ namespace RPC.EsiurTest
GuidVal = ByteString.CopyFrom(Guid ?? new byte[0]),
I64Val = I64 ?? 0,
StrVal = Str,
Tag = Enum.Parse<Echo.Model.Grpc.Kind>(Tag.ToString(), true),
Tag = Enum.Parse<Client.Grpc.Kind>(Tag.ToString(), true),
U64Val = U64 ?? 0,
};
}
+8
View File
@@ -0,0 +1,8 @@
using System;
namespace Esiur {
public static class Generated {
public static Type[] Resources {get;} = new Type[] { typeof(Esiur.Tests.RPC.EsiurServer.Service),typeof(Esiur.Tests.RPC.EsiurServer.TestObject) };
public static Type[] Records { get; } = new Type[] { typeof(Esiur.Tests.RPC.EsiurServer.BusinessDocument),typeof(Esiur.Tests.RPC.EsiurServer.Attachment),typeof(Esiur.Tests.RPC.EsiurServer.Party),typeof(Esiur.Tests.RPC.EsiurServer.Address),typeof(Esiur.Tests.RPC.EsiurServer.DocumentHeader),typeof(Esiur.Tests.RPC.EsiurServer.LineItem),typeof(Esiur.Tests.RPC.EsiurServer.Variant),typeof(Esiur.Tests.RPC.EsiurServer.Payment) };
public static Type[] Enums { get; } = new Type[] { typeof(Esiur.Tests.RPC.EsiurServer.Currency),typeof(Esiur.Tests.RPC.EsiurServer.DocType),typeof(Esiur.Tests.RPC.EsiurServer.Kind),typeof(Esiur.Tests.RPC.EsiurServer.LineType),typeof(Esiur.Tests.RPC.EsiurServer.PaymentMethod) };
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
namespace RPC.SharedModel
namespace Esiur.Tests.RPC.Client.SharedModel
{
// ====================== Enums ======================
+1 -1
View File
@@ -2,7 +2,7 @@
package gpmodel;
option csharp_namespace = "Echo.Model.Grpc";
option csharp_namespace = "Esiur.Tests.RPC.Client.Grpc";
// ========================= Enums =========================
@@ -7,7 +7,7 @@ using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session;
namespace RPC.Client.Tests;
namespace Esiur.Tests.RPC.Client;
public class PerProcessNetMonitor : IDisposable
{
+2 -5
View File
@@ -1,13 +1,10 @@
using MQTTnet;
using RPC.Client.Tests;
using RPC.Client.Tests.Docs;
using RPC.Client.Tests.Events;
using Esiur.Tests.RPC.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static RPC.Client.Tests.Events.Websockets;
var results = new Dictionary<string, List<TestResults>>();
@@ -22,7 +19,7 @@ for (var i = 0; i < 10; i++)
{
var seed = 1000 + (i * 1000);
var docsWorkloads = new Dictionary<string, RPC.EsiurTest.BusinessDocument[]>();// RPC.Client.Tests.DocGenerator.BuildWorkloads(seed);
var docsWorkloads = new Dictionary<string, Esiur.Tests.RPC.EsiurServer.BusinessDocument[]>();// RPC.Client.Tests.DocGenerator.BuildWorkloads(seed);
var dataWorkLoads = Shared.BuildBytesWorkLoads(seed);
var intWorkloads = Shared.BuildIntWorkloads(seed);
@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RPC.Client.Tests
namespace Esiur.Tests.RPC.Client
{
internal class Shared
{
@@ -13,7 +13,7 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace RPC.Client.Tests.Docs
namespace Esiur.Tests.RPC.Client
{
public static class SignalRTest
{
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Text;
namespace RPC.Client.Tests
namespace Esiur.Tests.RPC.Client
{
public class TestResults
{
@@ -1,114 +0,0 @@
using Esiur.Net.Sockets;
using Esiur.Resource;
using RPC.EsiurTest;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace RPC.Client.Tests.Events
{
public class EsiurEventsTest
{
public enum TestType
{
Chunk,
Event,
Property
}
public static async Task DoTest(TestType type, string address, int count, int size, int delay)
{
var rt = new TestResults();
//using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
//mon.Start();
var service = await Warehouse.Default.Get<Service>(address);
var sock = service.ResourceConnection.Socket as TcpSocket;
//await Task.Delay(3000);
//var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
if (type == TestType.Event)
service.EventTest(count, size, delay);
else if (type == TestType.Property)
service.PropertyChangeTest(count, size, delay);
else if (type == TestType.Chunk)
service.ChunkTest(count, size, delay);
var crx = sock.BytesReceived;
var ctx = sock.BytesSent;
Console.WriteLine($"Handshake {ctx}/{crx}");
await Task.Delay(7000 + (count * size));
crx = sock.BytesReceived - crx;
ctx = sock.BytesSent - ctx;
//(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
//Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
Console.WriteLine($"Results {ctx}/{crx}");
}
public static async Task DoEventTest(string address, int count, int size, int delay)
{
var rt = new TestResults();
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
mon.Start();
var service = await Warehouse.Default.Get<Service>(address);
await service.EventTest(count, size, delay);
await Task.Delay(3000);
var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
Console.WriteLine($"Handshake {ctx}/{crx}");
await Task.Delay(7000 + (count * delay));
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
}
public static async Task DoPropertyTest(string address, int count, int size, int delay)
{
var rt = new TestResults();
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
mon.Start();
var service = await Warehouse.Default.Get<Service>(address);
await Task.Delay(3000);
var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
Console.WriteLine($"Handshake {ctx}/{crx}");
await service.PropertyChangeTest(count, size, delay);
await Task.Delay(3000 + (size * delay));
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
}
}
}
-128
View File
@@ -1,128 +0,0 @@
// Benchmark MQTT client helper
// Usage: call BenchmarkClient.RunAsync("localhost", 1883, "welcome/topic/100/1024/0");
// This class is not an executable by itself to avoid duplicate entry points in the project.
using MQTTnet;
using MQTTnet.Protocol;
using RPC.Client.Tests;
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public static class MQTTTest
{
public static async Task DoTest(string brokerHost = "localhost", int brokerPort = 1883, string topic = "test/topic/100/100/100", CancellationToken cancellationToken = default)
{
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
mon.Start();
var factory = new MqttClientFactory();
using var mqttClient = factory.CreateMqttClient();
var options = new MqttClientOptionsBuilder()
.WithTcpServer(brokerHost, brokerPort)
.WithCleanSession()
.Build();
long receivedMessages = 0;
long receivedBytes = 0;
DateTime? firstReceivedAt = null;
DateTime? lastReceivedAt = null;
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
mqttClient.ApplicationMessageReceivedAsync += (e =>
{
var now = DateTime.UtcNow;
Interlocked.Increment(ref receivedMessages);
Interlocked.Add(ref receivedBytes, e.ApplicationMessage?.Payload.Length ?? 0);
if (firstReceivedAt == null)
{
firstReceivedAt = now;
}
lastReceivedAt = now;
// If expected count encoded in topic, check completion
var segments = topic.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (segments.Length >= 5 && int.TryParse(segments[2], out var expected) && expected > 0)
{
if (Interlocked.Read(ref receivedMessages) >= expected)
{
tcs.TrySetResult(true);
}
}
return Task.CompletedTask;
});
mqttClient.ConnectedAsync += (async e =>
{
Console.WriteLine($"Connected to {brokerHost}:{brokerPort}");
await mqttClient.SubscribeAsync(new MqttClientSubscribeOptionsBuilder()
.WithTopicFilter(f => { f.WithTopic(topic).WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtMostOnce); })
.Build());
Console.WriteLine($"Subscribed to topic '{topic}'");
});
//mqttClient.DisconnectedAsync += (e => Console.WriteLine("Disconnected from broker"));
await mqttClient.ConnectAsync(options, cancellationToken);
await Task.Delay(1000);
var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
Console.WriteLine($"Handshake {ctx}/{crx}");
// If no expected count, wait until cancelled
var segmentsCheck = topic.Split('/', StringSplitOptions.RemoveEmptyEntries);
Task waitTask;
if (segmentsCheck.Length >= 5 && int.TryParse(segmentsCheck[2], out var expectedCount) && expectedCount > 0)
{
// wait for expected messages or cancellation
waitTask = Task.WhenAny(tcs.Task, Task.Run(() => { var ct = new CancellationTokenSource(); cancellationToken.Register(ct.Cancel); return Task.Delay(Timeout.Infinite, ct.Token); })).Unwrap();
await waitTask;
}
else
{
// wait until cancellation
try
{
await Task.Delay(Timeout.Infinite, cancellationToken);
}
catch (TaskCanceledException) { }
}
// compute results
var totalMsgs = Interlocked.Read(ref receivedMessages);
var totalBytes = Interlocked.Read(ref receivedBytes);
var duration = (lastReceivedAt.HasValue && firstReceivedAt.HasValue) ? (lastReceivedAt.Value - firstReceivedAt.Value) : TimeSpan.Zero;
Console.WriteLine("--- Benchmark results ---");
Console.WriteLine($"Messages received: {totalMsgs}");
Console.WriteLine($"Total bytes: {totalBytes}");
Console.WriteLine($"Duration (first->last): {duration.TotalSeconds:F3} s");
Console.WriteLine($"Messages/sec: {(duration.TotalSeconds > 0 ? (totalMsgs / duration.TotalSeconds) : 0):F3}");
Console.WriteLine($"Bytes/sec: {(duration.TotalSeconds > 0 ? (totalBytes / duration.TotalSeconds) : 0):F3}");
await Task.Delay(2000);
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
try
{
await mqttClient.DisconnectAsync();
}
catch { }
}
}
-142
View File
@@ -1,142 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
namespace RPC.Client.Tests.Events;
// Simple SSE client helper for benchmarking the /sse endpoint.
// Usage:
// var opts = new SseOptions { MessagesPerBurst = 10, MessageSize = 512, IntervalMs = 10, BurstIntervalMs = 1000 };
// await SseClient.ConnectAsync("http://localhost:5000/sse", opts, CancellationToken.None,
// onMessage: data => Console.WriteLine("msg:" + data.Length), onOpen: () => Console.WriteLine("open"), onError: ex => Console.WriteLine(ex));
public static class SseClient
{
public static async Task DoTest(string baseUrl, SseOptions options, CancellationToken ct,
Action<string>? onMessage = null, Action? onOpen = null, Action<Exception>? onError = null)
{
Console.WriteLine("Starting SSE client..." );
options ??= new SseOptions();
var url = BuildUrl(baseUrl, options);
using var http = new HttpClient() { Timeout = Timeout.InfiniteTimeSpan };
var req = new HttpRequestMessage(HttpMethod.Get, url);
req.Headers.Accept.Clear();
req.Headers.Accept.ParseAdd("text/event-stream");
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
mon.Start();
long tx = 0, rx = 0, ctx = 0, crx = 0;// = mon.GetDiff(0, 0);
try
{
using var resp = await http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false);
resp.EnsureSuccessStatusCode();
await Task.Delay(1000);
(tx, rx, ctx, crx) = mon.GetDiff(0, 0);
Console.WriteLine($"Handshake {ctx}/{crx}");
onOpen?.Invoke();
using var stream = await resp.Content.ReadAsStreamAsync(ct).ConfigureAwait(false);
using var reader = new System.IO.StreamReader(stream, Encoding.UTF8);
var dataBuilder = new StringBuilder();
long totalRxBytes = 0;
while (!ct.IsCancellationRequested)
{
var readTask = reader.ReadLineAsync();
var completed = await Task.WhenAny(readTask, Task.Delay(Timeout.InfiniteTimeSpan, ct)).ConfigureAwait(false);
if (completed != readTask)
break; // cancelled
var line = await readTask!; // safe because completed
if (line is null)
break; // stream ended
if (line.Length == 0)
{
// dispatch event
if (dataBuilder.Length > 0)
{
// Remove trailing newline added while parsing
if (dataBuilder.Length > 0 && dataBuilder[dataBuilder.Length - 1] == '\n')
dataBuilder.Length--;
var data = dataBuilder.ToString();
onMessage?.Invoke(data);
dataBuilder.Clear();
}
continue;
}
// comments start with ':' -> ignore
if (line[0] == ':')
continue;
// data: lines (may be multi-line)
if (line.StartsWith("data:"))
{
var payload = line.Length > 5 ? line.Substring(5) : string.Empty;
// If the payload starts with a single space per SSE spec, trim one leading space
if (payload.Length > 0 && payload[0] == ' ') payload = payload.Substring(1);
dataBuilder.Append(payload);
dataBuilder.Append('\n');
}
// other fields (event:, id:, retry:) are ignored by this simple client
}
}
catch (OperationCanceledException)
{
// cancellation requested by caller
}
catch (Exception ex)
{
onError?.Invoke(ex);
}
await Task.Delay(2000);
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
Console.WriteLine($"Results {ctx}/{crx} Total: {tx}/{rx}");
}
static string BuildUrl(string baseUrl, SseOptions opts)
{
var sb = new StringBuilder();
sb.Append(baseUrl);
var sep = baseUrl.Contains('?') ? '&' : '?';
sb.Append(sep);
sb.Append("messagesPerBurst=").Append(opts.MessagesPerBurst);
sb.Append("&messageSize=").Append(opts.MessageSize);
sb.Append("&intervalMs=").Append(opts.IntervalMs);
sb.Append("&burstIntervalMs=").Append(opts.BurstIntervalMs);
return sb.ToString();
}
}
public class SseOptions
{
public int MessagesPerBurst { get; set; } = 1;
public int MessageSize { get; set; } = 100;
public int IntervalMs { get; set; } = 0;
public int BurstIntervalMs { get; set; } = 1000;
}
-146
View File
@@ -1,146 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
namespace RPC.Client.Tests.Events
{
public class Websockets
{
public record BenchmarkResult(int? Requested, int Received, long TotalBytes, long DurationMs, double MessagesPerSec, double BytesPerSec);
public static class WSClient
{
public static async Task<BenchmarkResult> DoTest(
string url,
int? count = 100,
int size = 128,
int intervalMs = 1000,
bool binary = true,
bool useQuery = true,
CancellationToken cancellationToken = default)
{
Console.WriteLine("Starting Websockets benchmark...");
// prepare connect url
var connectUrl = url;
if (useQuery)
{
var qs = new QueryBuilder();
if (count.HasValue) qs.Add("count", count.Value.ToString());
qs.Add("size", size.ToString());
qs.Add("intervalMs", intervalMs.ToString());
qs.Add("binary", binary.ToString().ToLowerInvariant());
connectUrl += (url.Contains('?') ? "&" : "?") + qs.ToString();
}
await Task.Delay(2000);
using var mon = new PerProcessNetMonitor(Process.GetCurrentProcess().Id);
mon.Start();
using var client = new ClientWebSocket();
await client.ConnectAsync(new Uri(connectUrl), cancellationToken);
// if not using query, send init JSON
if (!useQuery)
{
var init = JsonSerializer.Serialize(new { count, size, intervalMs, binary });
var initBytes = Encoding.UTF8.GetBytes(init);
await client.SendAsync(new ArraySegment<byte>(initBytes), WebSocketMessageType.Text, true, cancellationToken);
}
var bufferSize = Math.Max(8192, size + 4096);
var buffer = new byte[bufferSize];
int received = 0;
long totalBytes = 0;
long firstMs = 0;
long lastMs = 0;
await Task.Delay(2000);
var (tx, rx, ctx, crx) = mon.GetDiff(0, 0);
Console.WriteLine($"Handshake {ctx}/{crx}");
long totalRxBytes = 0;
try
{
while (client.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested)
{
if (count.HasValue && received >= count.Value) break;
// Receive a full message (handle fragmentation)
int messageBytes = 0;
WebSocketReceiveResult result;
do
{
result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
//(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
//totalRxBytes += crx;
if (result.MessageType == WebSocketMessageType.Close) goto EndReceive;
messageBytes += result.Count;
} while (!result.EndOfMessage);
var now = DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
if (received == 0) firstMs = now;
lastMs = now;
received++;
totalBytes += messageBytes;
if (count.HasValue && received >= count.Value) break;
}
}
catch (OperationCanceledException) { /* cancelled by caller */ }
catch (WebSocketException) { /* network error */ }
EndReceive:
if (client.State == WebSocketState.Open || client.State == WebSocketState.CloseReceived)
{
try { await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client closing", CancellationToken.None); } catch { }
}
var durationMs = (firstMs != 0 && lastMs >= firstMs) ? (lastMs - firstMs) : 0;
var msgsPerSec = durationMs > 0 ? (received * 1000.0) / durationMs : (double)received;
var bytesPerSec = durationMs > 0 ? (totalBytes * 1000.0) / durationMs : (double)totalBytes;
Console.WriteLine("Total RX bytes (monitor): " + totalRxBytes);
await Task.Delay(2000);
(tx, rx, ctx, crx) = mon.GetDiff(tx, rx);
Console.WriteLine($"Total Mon {tx}/{rx} {ctx}/{crx} {received}");
mon.Stop();
return new BenchmarkResult(count, received, totalBytes, durationMs, msgsPerSec, bytesPerSec);
}
// minimal QueryString builder to avoid extra deps
private class QueryBuilder
{
private readonly StringBuilder _sb = new();
private bool _first = true;
public void Add(string name, string value)
{
if (!_first) _sb.Append('&');
_first = false;
_sb.Append(Uri.EscapeDataString(name));
_sb.Append('=');
_sb.Append(Uri.EscapeDataString(value));
}
public override string ToString() => _sb.ToString();
}
}
}
}
-352
View File
@@ -1,352 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace RPC.Client.Tests.Queue;
using Esiur.Core;
using System;
using System.Collections.Generic;
using System.Linq;
public static class EsiurQueueEval
{
public static class EvalPrinter
{
public static void Print(EsiurQueueEval.EvalResult r)
{
Console.WriteLine("=== Evaluation Result ===");
Console.WriteLine($"α (HasResource probability): {r.Alpha:F3}");
Console.WriteLine($"λ̂ (effective arrival rate): {r.LambdaEventsPerSecond:F2} events/s");
Console.WriteLine();
PrintLatencyTable(r.Latency);
Console.WriteLine();
PrintValidation(r.Validation);
if (r.FlushSizeStats != null)
{
Console.WriteLine();
PrintStats("Flush size (≤ window)", r.FlushSizeStats);
}
}
private static void PrintLatencyTable(EsiurQueueEval.LatencyDecomposition l)
{
Console.WriteLine("Latency Decomposition (ms)");
Console.WriteLine("-----------------------------------------------");
Console.WriteLine($"{"Metric",-16} {"Mean",8} {"P50",8} {"P95",8} {"P99",8} {"Max",8}");
Console.WriteLine("-----------------------------------------------");
PrintRow("Readiness R", l.ReadinessMs);
PrintRow("HOL Δ", l.HolMs);
PrintRow("End-to-End D", l.EndToEndMs);
Console.WriteLine("-----------------------------------------------");
}
private static void PrintValidation(EsiurQueueEval.ModelValidation v)
{
Console.WriteLine("Resequencing Model Validation");
Console.WriteLine("-----------------------------------------------");
Console.WriteLine("Absolute error |d d̂| (ms)");
PrintStats("Error", v.AbsErrorMs);
if (v.MaxNegativeSlackMs > 0)
{
Console.WriteLine($"Max negative slack (delivered earlier than model): {v.MaxNegativeSlackMs:F4} ms");
}
else
{
Console.WriteLine("No negative slack observed (model is conservative).");
}
}
private static void PrintRow(string name, EsiurQueueEval.Stats s)
{
Console.WriteLine(
$"{name,-16} " +
$"{s.Mean,8:F2} " +
$"{s.P50,8:F2} " +
$"{s.P95,8:F2} " +
$"{s.P99,8:F2} " +
$"{s.Max,8:F2}"
);
}
private static void PrintStats(string name, EsiurQueueEval.Stats s)
{
Console.WriteLine(
$"{name,-20} " +
$"mean={s.Mean,8:F02} " +
$"p50={s.P50,8:F02} " +
$"p95={s.P95,8:F02} " +
$"p99={s.P99,8:F02} " +
$"max={s.Max,8:F02}"
);
}
}
public sealed record Stats(double Mean, double P50, double P95, double P99, double Max);
public sealed record LatencyDecomposition(Stats ReadinessMs, Stats HolMs, Stats EndToEndMs);
public sealed record ModelValidation(
Stats AbsErrorMs,
double MaxNegativeSlackMs, // worst case where Delivered < predicted (if happens)
int Count);
public sealed record EvalResult(
double Alpha,
double LambdaEventsPerSecond,
double MuEventsPerSecond, // <-- NEW
LatencyDecomposition Latency,
ModelValidation Validation,
Stats QueueLength,
Stats? FlushSizeStats);
/// <summary>
/// Evaluates Esiur fork-join readiness + in-order resequencing using in-memory items.
/// Assumes items refer to a single ordered stream (per resource queue).
/// </summary>
public static EvalResult Evaluate<T>(
IReadOnlyList<AsyncQueueItem<T>> items,
double flushWindowMs = 0.5,
bool computeFlush = true)
{
if (items == null) throw new ArgumentNullException(nameof(items));
if (items.Count == 0) throw new ArgumentException("items is empty.");
// Ensure deterministic order: prefer Sequence, then Arrival timestamp
var ordered = items
.OrderBy(x => x.Sequence)
.ThenBy(x => x.Arrival)
.ToArray();
int n = ordered.Length;
// Latency components in milliseconds
var readiness = new double[n]; // R = r-a
var hol = new double[n]; // Δ = d-r
var endToEnd = new double[n]; // D = d-a
int resCount = 0;
for (int i = 0; i < n; i++)
{
var e = ordered[i];
double Rms = (e.Ready - e.Arrival).TotalMilliseconds;
double Hms = (e.Delivered - e.Ready).TotalMilliseconds;
double Dms = (e.Delivered - e.Arrival).TotalMilliseconds;
// Robustness against logging placement or clock issues
if (Rms < 0) Rms = 0;
if (Hms < 0) Hms = 0;
if (Dms < 0) Dms = 0;
readiness[i] = Rms;
hol[i] = Hms;
endToEnd[i] = Dms;
if (e.HasResource) resCount++;
}
// α = P(HasResource)
double alpha = (double)resCount / n;
// Effective arrival rate λ̂ from arrival timeline
double lambda = EstimateLambda(ordered);
// Effective departure / readiness rate μ̂ from delivery timeline
double mu = EstimateMu(ordered);
var latency = new LatencyDecomposition(
ReadinessMs: ComputeStats(readiness),
HolMs: ComputeStats(hol),
EndToEndMs: ComputeStats(endToEnd));
// --- Resequencing validation: d_hat_i = max(r_i, d_hat_{i-1}) ---
// Use DateTime ticks for exactness, then convert to ms.
var absErrMs = new double[n];
long prevPredictedTicks = long.MinValue;
double maxNegativeSlackMs = 0;
for (int i = 0; i < n; i++)
{
long ri = ordered[i].Ready.Ticks;
long predicted = (i == 0)
? ri
: Math.Max(ri, prevPredictedTicks);
prevPredictedTicks = predicted;
long observed = ordered[i].Delivered.Ticks;
long errTicks = Math.Abs(observed - predicted);
absErrMs[i] = TicksToMs(errTicks);
// If observed delivery occurs earlier than model predicts (shouldn't, but track it)
long slackTicks = observed - predicted; // negative => earlier than predicted
if (slackTicks < 0)
{
double slackMs = TicksToMs(-slackTicks);
if (slackMs > maxNegativeSlackMs) maxNegativeSlackMs = slackMs;
}
}
var validation = new ModelValidation(
AbsErrorMs: ComputeStats(absErrMs),
MaxNegativeSlackMs: maxNegativeSlackMs,
Count: n);
// --- Flush sizes (optional): consecutive deliveries within window ---
//Stats? flushStats = null;
//if (computeFlush)
//{
// var flushSizes = ComputeFlushSizes(ordered, flushWindowMs);
// flushStats = ComputeStats(flushSizes.Select(x => (double)x).ToArray());
//}
var queueLength = ComputeStats(ordered.Select(x => (double)x.NotificationsCountWaitingInTheQueueAtEnqueueing).ToArray());
var flushStats = ComputeStats(ordered.GroupBy(x => x.FlushId).Select(x => (double)x.First().BatchSize).ToArray());
return new EvalResult(alpha, lambda, mu, latency, validation, queueLength, flushStats);
}
// ---------------- Helpers ----------------
private static double EstimateLambda<T>(AsyncQueueItem<T>[] ordered)
{
if (ordered.Length < 2) return 0;
DateTime first = ordered[0].Arrival;
DateTime last = ordered[^1].Arrival;
double seconds = (last - first).TotalSeconds;
if (seconds <= 0) return 0;
// N-1 arrivals over observed interval
return (ordered.Length - 1) / seconds;
}
private static double TicksToMs(long ticks) => ticks / 10_000.0; // 1 tick = 100ns
private static int[] ComputeFlushSizes<T>(AsyncQueueItem<T>[] ordered, double windowMs)
{
var sizes = new List<int>(ordered.Length);
long windowTicks = (long)(windowMs * 10_000.0);
int i = 0;
while (i < ordered.Length)
{
int j = i + 1;
long baseD = ordered[i].Delivered.Ticks;
while (j < ordered.Length)
{
long dj = ordered[j].Delivered.Ticks;
if (Math.Abs(dj - baseD) <= windowTicks) j++;
else break;
}
sizes.Add(j - i);
i = j;
}
return sizes.ToArray();
}
private static Stats ComputeStats(double[] values)
{
if (values.Length == 0) return new Stats(0, 0, 0, 0, 0);
double mean = values.Average();
double max = values.Max();
var sorted = (double[])values.Clone();
Array.Sort(sorted);
return new Stats(
Mean: mean,
P50: QuantileSorted(sorted, 0.50),
P95: QuantileSorted(sorted, 0.95),
P99: QuantileSorted(sorted, 0.99),
Max: max);
}
// Linear interpolation quantile
private static double QuantileSorted(double[] sorted, double q)
{
if (sorted.Length == 1) return sorted[0];
double pos = (sorted.Length - 1) * q;
int lo = (int)Math.Floor(pos);
int hi = (int)Math.Ceiling(pos);
if (lo == hi) return sorted[lo];
double frac = pos - lo;
return sorted[lo] + (sorted[hi] - sorted[lo]) * frac;
}
// Compute the element-wise average of a sequence of EvalResult
public static EvalResult Average(IEnumerable<EvalResult> results)
{
if (results == null) throw new ArgumentNullException(nameof(results));
var arr = results.ToArray();
if (arr.Length == 0) throw new ArgumentException("results is empty.", nameof(results));
double avgAlpha = arr.Average(r => r.Alpha);
double avgLambda = arr.Average(r => r.LambdaEventsPerSecond);
double avgMu = arr.Average(r => r.MuEventsPerSecond);
Stats avgReadiness = AverageStats(arr.Select(r => r.Latency.ReadinessMs));
Stats avgHol = AverageStats(arr.Select(r => r.Latency.HolMs));
Stats avgE2E = AverageStats(arr.Select(r => r.Latency.EndToEndMs));
var avgLatency = new LatencyDecomposition(avgReadiness, avgHol, avgE2E);
Stats avgAbsError = AverageStats(arr.Select(r => r.Validation.AbsErrorMs));
double worstNegativeSlack = arr.Max(r => r.Validation.MaxNegativeSlackMs);
int totalCount = arr.Sum(r => r.Validation.Count);
var avgValidation = new ModelValidation(avgAbsError, worstNegativeSlack, totalCount);
Stats? avgFlush = null;
var flushStatsSeq = arr.Select(r => r.FlushSizeStats).Where(s => s != null).Select(s => s!).ToArray();
if (flushStatsSeq.Length > 0) avgFlush = AverageStats(flushStatsSeq);
var avgQueue = AverageStats(arr.Select(x => x.QueueLength));
return new EvalResult(avgAlpha, avgLambda, avgMu, avgLatency, avgValidation, avgQueue, avgFlush);
}
private static double EstimateMu<T>(AsyncQueueItem<T>[] ordered)
{
if (ordered.Length < 2) return 0;
DateTime first = ordered[0].Delivered;
DateTime last = ordered[^1].Delivered;
double seconds = (last - first).TotalSeconds;
if (seconds <= 0) return 0;
// N-1 completions over observed interval
return (ordered.Length - 1) / seconds;
}
private static Stats AverageStats(IEnumerable<Stats> seq)
{
var arr = seq.ToArray();
if (arr.Length == 0) return new Stats(0, 0, 0, 0, 0);
return new Stats(
Mean: arr.Average(s => s.Mean),
P50: arr.Average(s => s.P50),
P95: arr.Average(s => s.P95),
P99: arr.Average(s => s.P99),
Max: arr.Average(s => s.Max));
}
}
@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace RPC.Client.Tests.Docs;
namespace Esiur.Tests.RPC.Client;
public class ThriftTest
{
@@ -25,7 +25,7 @@ public class ThriftTest
using var socket = new Thrift.Transport.Client.TSocketTransport(host, port, new Thrift.TConfiguration());
//await socket.OpenAsync(new CancellationToken());
var proto = new Thrift.Protocol.TBinaryProtocol(socket);
var service = new Echo.ThriftModel.EchoService.Client(proto);
var service = new EchoService.Client(proto);
Thread.Sleep(3000);