mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-02 08:50:18 -04:00
Reduce DeepClone allocation churn Add fast path for single ID properties | Method | GOs | Baseline | After | change time | Baseline alloc | After alloc | change alloc | |---|---|---|---|---|---|---|---| | `CalculateDifferences` | 200 | 3.405 ms | **1.471 ms** | **-57%** | 3.78 MB | **1.99 MB** | **-47%** | | `ApplyPatch` | 200 | 1.669 ms | **0.639 ms** | **-62%** | 3.01 MB | **0.99 MB** | **-67%** | || | `CalculateDifferences` | 1000 | 41.39 ms | **17.38 ms** | **-58%** | 18.55 MB | **9.38 MB** | **-49%** | | `ApplyPatch` | 1000 | 24.34 ms | **8.171 ms** | **-66%** | 14.84 MB | **4.95 MB** | **-67%** | Roughly 50% faster
103 lines
3.3 KiB
C#
103 lines
3.3 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.Json.Nodes;
|
|
|
|
namespace JsonTests.Diff;
|
|
|
|
[TestClass]
|
|
public class ImmutabilityTest
|
|
{
|
|
private static HashSet<Json.TrackedObjectDefinition> BuildDefinitions() =>
|
|
[
|
|
Json.TrackedObjectDefinition.CreatePresenceBasedDefinition(
|
|
type: "Root", requiredFields: ["company"], allowedAsRoot: true
|
|
),
|
|
Json.TrackedObjectDefinition.CreatePresenceBasedDefinition(
|
|
type: "Department", requiredFields: ["id", "name"], idProperty: "id", parentType: "Root"
|
|
),
|
|
Json.TrackedObjectDefinition.CreatePresenceBasedDefinition(
|
|
type: "Employee", requiredFields: ["id", "name", "role"], idProperty: "id", parentType: "Department"
|
|
),
|
|
];
|
|
|
|
private static JsonObject Source() => JsonNode.Parse(
|
|
"""
|
|
{
|
|
"company": { "departments": [
|
|
{ "id": 1, "name": "Engineering", "employees": [
|
|
{ "id": 101, "name": "Alice", "role": "Developer" }
|
|
]}
|
|
]}
|
|
}
|
|
""" ).AsObject();
|
|
|
|
private static JsonObject Target() => JsonNode.Parse(
|
|
"""
|
|
{
|
|
"company": { "departments": [
|
|
{ "id": 1, "name": "Engineering", "budget": 500000, "employees": [
|
|
{ "id": 101, "name": "Alice", "role": "Senior Developer" }
|
|
]},
|
|
{ "id": 2, "name": "Marketing", "employees": [
|
|
{ "id": 201, "name": "Charlie", "role": "Manager" }
|
|
]}
|
|
]}
|
|
}
|
|
""" ).AsObject();
|
|
|
|
[TestMethod]
|
|
public void CalculateDifferences_DoesNotMutateOldRoot()
|
|
{
|
|
var defs = BuildDefinitions();
|
|
var oldRoot = Source();
|
|
var before = oldRoot.ToJsonString();
|
|
Json.CalculateDifferences( oldRoot, Target(), defs );
|
|
Assert.AreEqual( before, oldRoot.ToJsonString() );
|
|
}
|
|
|
|
[TestMethod]
|
|
public void CalculateDifferences_DoesNotMutateNewRoot()
|
|
{
|
|
var defs = BuildDefinitions();
|
|
var newRoot = Target();
|
|
var before = newRoot.ToJsonString();
|
|
Json.CalculateDifferences( Source(), newRoot, defs );
|
|
Assert.AreEqual( before, newRoot.ToJsonString() );
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ApplyPatch_DoesNotMutateSource()
|
|
{
|
|
var defs = BuildDefinitions();
|
|
var source = Source();
|
|
var patch = Json.CalculateDifferences( source, Target(), defs );
|
|
var before = source.ToJsonString();
|
|
Json.ApplyPatch( source, patch, defs );
|
|
Assert.AreEqual( before, source.ToJsonString() );
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ApplyPatch_PatchIsReusable()
|
|
{
|
|
var defs = BuildDefinitions();
|
|
var target = Target();
|
|
var patch = Json.CalculateDifferences( Source(), target, defs );
|
|
Assert.IsTrue( JsonNode.DeepEquals( Json.ApplyPatch( Source(), patch, defs ), target ) );
|
|
Assert.IsTrue( JsonNode.DeepEquals( Json.ApplyPatch( Source(), patch, defs ), target ) );
|
|
}
|
|
|
|
[TestMethod]
|
|
public void AddedObject_DataDoesNotContainTrackedChildren()
|
|
{
|
|
// Dept 2 is added with employee 201 inside it. AddedObject.Data must not embed the
|
|
// employee — employees are tracked separately and would appear twice if included.
|
|
var defs = BuildDefinitions();
|
|
var patch = Json.CalculateDifferences( Source(), Target(), defs );
|
|
Assert.IsTrue( patch.AddedObjects.Any( a => a.Id.IdValue == "2" ), "Department 2 should be in AddedObjects" );
|
|
var addedDept = patch.AddedObjects.First( a => a.Id.IdValue == "2" );
|
|
if ( addedDept.Data.TryGetPropertyValue( "employees", out var emp ) )
|
|
Assert.AreEqual( 0, emp?.AsArray()?.Count ?? 0 );
|
|
Assert.IsTrue( JsonNode.DeepEquals( Json.ApplyPatch( Source(), patch, defs ), Target() ) );
|
|
}
|
|
}
|