Files
speckle-sharp-sdk/tests/Speckle.Sdk.Tests.Unit/Api/Operations/SerializationTests.cs
T
Jedd Morgan 5e0ea324c3 Re-introduced code analysers and fixed many violations (#92)
* Sdk

* Objects

* Supressed IDE warnings via editor config instead of nowarn

* Nullability and other warnings

* using

* Fixed warnings

* Important fix

* More fixes
2024-09-04 11:49:35 +00:00

269 lines
8.2 KiB
C#

using System.Drawing;
using NUnit.Framework;
using Speckle.Sdk.Host;
using Speckle.Sdk.Models;
using Speckle.Sdk.Tests.Unit.Host;
using Point = Speckle.Sdk.Tests.Unit.Host.Point;
namespace Speckle.Sdk.Tests.Unit.Api.Operations;
[TestFixture]
[TestOf(typeof(Sdk.Api.Operations))]
public class ObjectSerialization
{
[SetUp]
public void Setup()
{
TypeLoader.Reset();
TypeLoader.Initialize(typeof(Base).Assembly, typeof(DataChunk).Assembly, typeof(ColorMock).Assembly);
}
[Test]
public async Task IgnoreCircularReferences()
{
var pt = new Point(1, 2, 3);
pt["circle"] = pt;
var test = Sdk.Api.Operations.Serialize(pt);
var result = await Sdk.Api.Operations.Deserialize(test);
var circle = result["circle"];
Assert.That(circle, Is.Null);
}
[Test]
public async Task InterfacePropHandling()
{
Line tail = new() { Start = new Point(0, 0, 0), End = new Point(42, 42, 42) };
PolygonalFeline cat = new() { Tail = tail };
for (int i = 0; i < 10; i++)
{
cat.Claws[$"Claw number {i}"] = new Line
{
Start = new Point(i, i, i),
End = new Point(i + 3.14, i + 3.14, i + 3.14)
};
if (i % 2 == 0)
{
cat.Whiskers.Add(
new Line { Start = new Point(i / 2, i / 2, i / 2), End = new Point(i + 3.14, i + 3.14, i + 3.14) }
);
}
else
{
var brokenWhisker = new Polyline();
brokenWhisker.Points.Add(new Point(-i, 0, 0));
brokenWhisker.Points.Add(new Point(0, 0, 0));
brokenWhisker.Points.Add(new Point(i, 0, 0));
cat.Whiskers.Add(brokenWhisker);
}
cat.Fur[i] = new Line { Start = new Point(i, i, i), End = new Point(i + 3.14, i + 3.14, i + 3.14) };
}
var result = Sdk.Api.Operations.Serialize(cat);
var deserialisedFeline = await Sdk.Api.Operations.Deserialize(result);
Assert.That(deserialisedFeline.GetId(), Is.EqualTo(cat.GetId())); // If we're getting the same hash... we're probably fine!
}
[Test]
public async Task InheritanceTests()
{
var superPoint = new SuperPoint
{
X = 10,
Y = 10,
Z = 10,
W = 42
};
var str = Sdk.Api.Operations.Serialize(superPoint);
var sstr = await Sdk.Api.Operations.Deserialize(str);
Assert.That(sstr.speckle_type, Is.EqualTo(superPoint.speckle_type));
}
[Test]
public async Task ListDynamicProp()
{
var point = new Point();
var test = new List<Base>();
for (var i = 0; i < 100; i++)
{
test.Add(new SuperPoint { W = i });
}
point["test"] = test;
var str = Sdk.Api.Operations.Serialize(point);
var dsrls = await Sdk.Api.Operations.Deserialize(str);
var list = dsrls["test"] as List<object>; // NOTE: on dynamically added lists, we cannot infer the inner type and we always fall back to a generic list<object>.
Assert.That(list, Has.Count.EqualTo(100));
}
[Test]
public async Task ChunkSerialisation()
{
var baseBasedChunk = new DataChunk() { data = new() };
for (var i = 0; i < 200; i++)
{
baseBasedChunk.data.Add(new SuperPoint { W = i });
}
var stringBasedChunk = new DataChunk() { data = new() };
for (var i = 0; i < 200; i++)
{
stringBasedChunk.data.Add(i + "_hai");
}
var doubleBasedChunk = new DataChunk() { data = new() };
for (var i = 0; i < 200; i++)
{
doubleBasedChunk.data.Add(i + 0.33);
}
var baseChunkString = Sdk.Api.Operations.Serialize(baseBasedChunk);
var stringChunkString = Sdk.Api.Operations.Serialize(stringBasedChunk);
var doubleChunkString = Sdk.Api.Operations.Serialize(doubleBasedChunk);
var baseChunkDeserialised = (DataChunk)await Sdk.Api.Operations.Deserialize(baseChunkString);
var stringChunkDeserialised = (DataChunk)await Sdk.Api.Operations.Deserialize(stringChunkString);
var doubleChunkDeserialised = (DataChunk)await Sdk.Api.Operations.Deserialize(doubleChunkString);
Assert.That(baseChunkDeserialised.data, Has.Count.EqualTo(baseBasedChunk.data.Count));
Assert.That(stringChunkDeserialised.data, Has.Count.EqualTo(stringBasedChunk.data.Count));
Assert.That(doubleChunkDeserialised.data, Has.Count.EqualTo(doubleBasedChunk.data.Count));
}
[Test]
public async Task ObjectWithChunksSerialisation()
{
const int MAX_NUM = 2020;
var mesh = new FakeMesh { ArrayOfDoubles = new double[MAX_NUM], ArrayOfLegs = new TableLeg[MAX_NUM] };
var customChunk = new List<double>();
var defaultChunk = new List<double>();
for (int i = 0; i < MAX_NUM; i++)
{
mesh.Vertices.Add(i / 2);
customChunk.Add(i / 2);
defaultChunk.Add(i / 2);
mesh.Tables.Add(new Tabletop { length = 2000 });
mesh.ArrayOfDoubles[i] = i * 3.3;
mesh.ArrayOfLegs[i] = new TableLeg { height = 2 + i };
}
mesh["@(800)CustomChunk"] = customChunk;
mesh["@()DefaultChunk"] = defaultChunk;
var serialised = Sdk.Api.Operations.Serialize(mesh);
var deserialised = await Sdk.Api.Operations.Deserialize(serialised);
Assert.That(mesh.GetId(), Is.EqualTo(deserialised.GetId()));
}
[Test]
public void EmptyListSerialisationTests()
{
// NOTE: expected behaviour is that empty lists should serialize as empty lists. Don't ask why, it's complicated.
// Regarding chunkable empty lists, to prevent empty chunks, the expected behaviour is to have an empty lists, with no chunks inside.
var test = new Base();
test["@(5)emptyChunks"] = new List<object>();
test["emptyList"] = new List<object>();
test["@emptyDetachableList"] = new List<object>();
// Note: nested empty lists should be preserved.
test["nestedList"] = new List<object> { new List<object> { new List<object>() } };
test["@nestedDetachableList"] = new List<object> { new List<object> { new List<object>() } };
var serialised = Sdk.Api.Operations.Serialize(test);
var isCorrect =
serialised.Contains("\"@(5)emptyChunks\":[]")
&& serialised.Contains("\"emptyList\":[]")
&& serialised.Contains("\"@emptyDetachableList\":[]")
&& serialised.Contains("\"nestedList\":[[[]]]")
&& serialised.Contains("\"@nestedDetachableList\":[[[]]]");
Assert.That(isCorrect, Is.EqualTo(true));
}
[SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+DateMock")]
private class DateMock : Base
{
public DateTime TestField { get; set; }
}
[Test]
public async Task DateSerialisation()
{
var date = new DateTime(2020, 1, 14);
var mockBase = new DateMock { TestField = date };
var result = Sdk.Api.Operations.Serialize(mockBase);
var test = (DateMock)await Sdk.Api.Operations.Deserialize(result);
Assert.That(test.TestField, Is.EqualTo(date));
}
[SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+GUIDMock")]
private class GUIDMock : Base
{
public Guid TestField { get; set; }
}
[Test]
public async Task GuidSerialisation()
{
var guid = Guid.NewGuid();
var mockBase = new GUIDMock { TestField = guid };
var result = Sdk.Api.Operations.Serialize(mockBase);
var test = (GUIDMock)await Sdk.Api.Operations.Deserialize(result);
Assert.That(test.TestField, Is.EqualTo(guid));
}
[SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+ColorMock")]
private class ColorMock : Base
{
public Color TestField { get; set; }
}
[Test]
public async Task ColorSerialisation()
{
var color = Color.FromArgb(255, 4, 126, 251);
var mockBase = new ColorMock { TestField = color };
var result = Sdk.Api.Operations.Serialize(mockBase);
var test = (ColorMock)await Sdk.Api.Operations.Deserialize(result);
Assert.That(test.TestField, Is.EqualTo(color));
}
[SpeckleType("Speckle.Core.Tests.Unit.Api.Operations.ObjectSerialization+StringDateTimeRegressionMock")]
private class StringDateTimeRegressionMock : Base
{
public string TestField { get; set; }
}
[Test]
public async Task StringDateTimeRegression()
{
var mockBase = new StringDateTimeRegressionMock { TestField = "2021-11-12T11:32:01" };
var result = Sdk.Api.Operations.Serialize(mockBase);
var test = (StringDateTimeRegressionMock)await Sdk.Api.Operations.Deserialize(result);
Assert.That(test.TestField, Is.EqualTo(mockBase.TestField));
}
}