Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<PackageId>ModernUO.Serialization.Annotations</PackageId>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
<AssemblyVersion>4.2.0</AssemblyVersion>
<PackageVersion>4.2.0</PackageVersion>
<AssemblyVersion>4.3.0</AssemblyVersion>
<PackageVersion>4.3.0</PackageVersion>
<AssemblyName>ModernUO.Serialization.Annotations</AssemblyName>
<RootNamespace>ModernUO.Serialization</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand Down
107 changes: 74 additions & 33 deletions ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ namespace ModernUO.Serialization.Generator.Tests;
/// <summary>
/// Runs the generated AddToX/RemoveFromX/InsertIntoX/ReplaceInX/ClearX helpers against the
/// Snapshots/NullableCollections entity: removals and clears tolerate a null collection, adds create
/// it lazily (keeping a [SortedSetComparer]), and MarkDirty fires only when the collection changed.
/// it lazily (keeping a [SortedSetComparer]), MarkDirty fires only when the collection changed, and
/// helpers that can be a no-op return whether they changed anything.
/// </summary>
public class DataStructureMethodTests
{
private static string FixtureSource([CallerFilePath] string thisFile = "") =>
File.ReadAllText(
Path.Combine(Path.GetDirectoryName(thisFile)!, "Snapshots", "NullableCollections", "Input.cs")
);
private static string FixtureSource(string snapshot, [CallerFilePath] string thisFile = "") =>
File.ReadAllText(Path.Combine(Path.GetDirectoryName(thisFile)!, "Snapshots", snapshot, "Input.cs"));

private static string Run(string assemblyName, string source) =>
(string)SourceGeneratorTestHelper.CompileAndLoad(assemblyName, source)
.GetType("Server.TestContent.Driver")!
.GetMethod("Run")!
.Invoke(null, null)!;

private const string Driver = """
namespace Server.TestContent
Expand All @@ -28,42 +33,37 @@ public static string Run()
void Step(string name, object? state = null) =>
log.Append(name).Append('=').Append(item.DirtyCount).Append(state != null ? $":{state}" : "").Append(';');

item.RemoveFromCharges(1);
var removed = item.RemoveFromCharges(1) | item.RemoveFromKeywords("a") |
item.RemoveFromLabels(1) | item.RemoveFromNames("a");
item.RemoveFromChargesAt(0);
item.ClearCharges();
item.RemoveFromKeywords("a");
item.ClearKeywords();
item.RemoveFromLabels(1);
item.ClearLabels();
item.RemoveFromNames("a");
item.ClearNames();
Step("null", item.Charges == null && item.Keywords == null && item.Labels == null && item.Names == null);
Step("null", $"{removed}:{item.Charges == null && item.Keywords == null && item.Labels == null && item.Names == null}");

item.AddToCharges(5);
Step("addList");
item.InsertIntoCharges(0, 4);
Step("insert", string.Join(",", item.Charges!));

item.AddToKeywords("a");
Step("addSet");
item.AddToKeywords("a");
Step("addSetDuplicate");
Step("addSet", item.AddToKeywords("a"));
Step("addSetDuplicate", item.AddToKeywords("a"));

item.AddToLabels(1, "x");
Step("addDictionary");
item.AddToLabels(1, "y");
Step("addDictionaryDuplicate", item.Labels![1]);
Step("addDictionary", item.AddToLabels(1, "x"));
Step("addDictionaryDuplicate", $"{item.AddToLabels(1, "y")}:{item.Labels![1]}");
item.ReplaceInLabels(1, null);
Step("replace", item.Labels[1] ?? "null");

item.AddToNames("Bob");
item.AddToNames("BOB");
Step("comparer", item.Names!.Count);
Step("comparer", $"{item.AddToNames("BOB")}:{item.Names!.Count}");

item.RemoveFromKeywords("missing");
Step("removeMissing");
item.RemoveFromKeywords("a");
Step("remove");
Step("removeMissing", item.RemoveFromKeywords("missing"));
Step("remove", item.RemoveFromKeywords("a"));
Step("removeListMissing", item.RemoveFromCharges(9));
Step("removeList", item.RemoveFromCharges(4));
Step("removeDictionaryMissing", item.RemoveFromLabels(9));
Step("removeDictionary", item.RemoveFromLabels(1));
item.ClearKeywords();
Step("clearEmpty");
item.ClearCharges();
Expand All @@ -79,20 +79,61 @@ void Step(string name, object? state = null) =>
}
""";

// Without a dirty target the helpers still report whether they changed anything.
private const string UntrackedSource = """
using System.Collections.Generic;
using ModernUO.Serialization;

namespace Server.TestContent
{
[SerializationGenerator(0)]
public partial class UntrackedRecord
{
[SerializableField(0)]
private List<int> _values = new();

[SerializableField(1)]
private HashSet<int> _set = new();

[SerializableField(2)]
private Dictionary<int, string> _lookup = new();
}

public static class Driver
{
public static string Run()
{
var r = new UntrackedRecord();
r.AddToValues(1);
return string.Join(
",",
r.RemoveFromValues(2), r.RemoveFromValues(1),
r.AddToSet(1), r.AddToSet(1), r.RemoveFromSet(2), r.RemoveFromSet(1),
r.AddToLookup(1, "a"), r.AddToLookup(1, "b"), r.RemoveFromLookup(2), r.RemoveFromLookup(1)
);
}
}
}
""";

[Fact]
public void Helpers_TolerateNullAndMarkDirtyOnlyOnChange()
{
var assembly = SourceGeneratorTestHelper.CompileAndLoad("NullableCollections", FixtureSource() + Driver);

var result = (string)assembly.GetType("Server.TestContent.Driver")!
.GetMethod("Run")!
.Invoke(null, null)!;
Assert.Equal(
"null=0:False:True;addList=1;insert=2:4,5;addSet=3:True;addSetDuplicate=3:False;addDictionary=4:True;" +
"addDictionaryDuplicate=4:False:x;replace=5:null;comparer=6:False:1;removeMissing=6:False;remove=7:True;" +
"removeListMissing=7:False;removeList=8:True;removeDictionaryMissing=8:False;removeDictionary=9:True;" +
"clearEmpty=9;clear=10:0;replaceNull=1:z",
Run("NullableCollections", FixtureSource("NullableCollections") + Driver)
);
}

[Fact]
public void Helpers_ReportChangesWithoutADirtyTarget()
{
Assert.Equal(
"null=0:True;addList=1;insert=2:4,5;addSet=3;addSetDuplicate=3;addDictionary=4;" +
"addDictionaryDuplicate=4:x;replace=5:null;comparer=6:1;removeMissing=6;remove=7;" +
"clearEmpty=7;clear=8:0;replaceNull=1:z",
result
"False,True,True,False,False,True,True,False,False,True",
Run("UntrackedRecord", UntrackedSource)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,15 @@ public void AddToCharges(int value)
Server.ISerializableExtensions.MarkDirty(this);
}

public void RemoveFromCharges(int value)
public bool RemoveFromCharges(int value)
{
if (_charges?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void InsertIntoCharges(int index, int value)
Expand Down Expand Up @@ -101,21 +104,27 @@ public System.Collections.Generic.HashSet<string> Keywords
}
}

public void AddToKeywords(string value)
public bool AddToKeywords(string value)
{
_keywords ??= new System.Collections.Generic.HashSet<string>();
if (_keywords.Add(value))
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void RemoveFromKeywords(string value)
public bool RemoveFromKeywords(string value)
{
if (_keywords?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void ClearKeywords()
Expand All @@ -140,21 +149,27 @@ public System.Collections.Generic.Dictionary<int, string> Labels
}
}

public void AddToLabels(int key, string value)
public bool AddToLabels(int key, string value)
{
_labels ??= new System.Collections.Generic.Dictionary<int, string>();
if (_labels.TryAdd(key, value))
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void RemoveFromLabels(int key)
public bool RemoveFromLabels(int key)
{
if (_labels?.Remove(key) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void ReplaceInLabels(int key, string value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,27 @@ public System.Collections.Generic.Dictionary<int, string> Entries
}
}

public void AddToEntries(int key, string value)
public bool AddToEntries(int key, string value)
{
_entries ??= new System.Collections.Generic.Dictionary<int, string>();
if (_entries.TryAdd(key, value))
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void RemoveFromEntries(int key)
public bool RemoveFromEntries(int key)
{
if (_entries?.Remove(key) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void ReplaceInEntries(int key, string value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ public void AddToExtendedEntries(Server.TestContent.DerivedEntry value)
Server.ISerializableExtensions.MarkDirty(this);
}

public void RemoveFromExtendedEntries(Server.TestContent.DerivedEntry value)
public bool RemoveFromExtendedEntries(Server.TestContent.DerivedEntry value)
{
if (_extendedEntries?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void InsertIntoExtendedEntries(int index, Server.TestContent.DerivedEntry value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ public void AddToEntries(Server.TestContent.BaseEntry value)
Server.ISerializableExtensions.MarkDirty(this);
}

public void RemoveFromEntries(Server.TestContent.BaseEntry value)
public bool RemoveFromEntries(Server.TestContent.BaseEntry value)
{
if (_entries?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
return true;
}

return false;
}

public void InsertIntoEntries(int index, Server.TestContent.BaseEntry value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public void AddToValues(int value)
_values.Add(value);
}

public void RemoveFromValues(int value)
public bool RemoveFromValues(int value)
{
_values?.Remove(value);
return _values?.Remove(value) == true;
}

public void InsertIntoValues(int index, int value)
Expand Down Expand Up @@ -84,15 +84,15 @@ public System.Collections.Generic.Dictionary<int, string> Lookup
}
}

public void AddToLookup(int key, string value)
public bool AddToLookup(int key, string value)
{
_lookup ??= new System.Collections.Generic.Dictionary<int, string>();
_lookup.TryAdd(key, value);
return _lookup.TryAdd(key, value);
}

public void RemoveFromLookup(int key)
public bool RemoveFromLookup(int key)
{
_lookup?.Remove(key);
return _lookup?.Remove(key) == true;
}

public void ReplaceInLookup(int key, string value)
Expand Down
Loading
Loading