using AyCode.Core.Serializers.Binaries; using AyCode.Core.Tests.TestModels; namespace AyCode.Core.Tests.Serialization; [TestClass] public class AcBinarySerializerSGenNullComplexPropertyTests { [TestMethod] [DataRow(true, true)] [DataRow(true, false)] [DataRow(false, false)] [DataRow(false, true)] public void Serialize_SGenComplexPropertyNull_DoesNotThrow_AndRoundTripsAsNull(bool useSgen, bool fastMode) { var model = new SGenNullComplexParent { Id = 7, Customer = null!, Note = "regression" }; var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default; options.UseGeneratedCode = useSgen; var bytes = AcBinarySerializer.Serialize(model, options); var roundTrip = AcBinaryDeserializer.Deserialize(bytes, options); Assert.IsNotNull(roundTrip); Assert.AreEqual(model.Id, roundTrip.Id); Assert.AreEqual(model.Note, roundTrip.Note); Assert.IsNull(roundTrip.Customer, "complex reference property must round-trip as null when source was null " + "(regression for SGen WriteObjectGenerated fallback else-branch null-check)"); Assert.IsTrue(System.Array.IndexOf(bytes, (byte)BinaryTypeCode.PropertySkip) >= 0, "writer must emit PropertySkip marker on the null Customer slot " + "(deeper verification: confirms the fix took the PropertySkip path, " + "not an unrelated null-safe code path)"); } [TestMethod] [DataRow(true, true)] [DataRow(true, false)] [DataRow(false, false)] [DataRow(false, true)] public void Serialize_SGenComplexPropertyNonNull_RoundTripsCorrectly(bool useSgen, bool fastMode) { var model = new SGenNullComplexParent { Id = 13, Customer = new NonGeneratedComplexCustomer { Id = 42, Name = "child" }, Note = "positive" }; var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default; options.UseGeneratedCode = useSgen; var bytes = AcBinarySerializer.Serialize(model, options); var roundTrip = AcBinaryDeserializer.Deserialize(bytes, options); Assert.IsNotNull(roundTrip); Assert.AreEqual(model.Id, roundTrip.Id); Assert.AreEqual(model.Note, roundTrip.Note); Assert.IsNotNull(roundTrip.Customer, "non-null complex reference property must round-trip (null-check fix must not break the non-null path)"); Assert.AreEqual(model.Customer.Id, roundTrip.Customer.Id); Assert.AreEqual(model.Customer.Name, roundTrip.Customer.Name); } [TestMethod] [DataRow(true, true)] [DataRow(true, false)] [DataRow(false, false)] [DataRow(false, true)] public void Serialize_SGenCollectionPropertyNull_DoesNotThrow_AndRoundTripsAsNull(bool useSgen, bool fastMode) { var model = new SGenNullCollectionParent { Id = 11, Items = null!, Note = "regression-collection" }; var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default; options.UseGeneratedCode = useSgen; var bytes = AcBinarySerializer.Serialize(model, options); var roundTrip = AcBinaryDeserializer.Deserialize(bytes, options); Assert.IsNotNull(roundTrip); Assert.AreEqual(model.Id, roundTrip.Id); Assert.AreEqual(model.Note, roundTrip.Note); Assert.IsNull(roundTrip.Items, "collection property (with non-SGen element type) must round-trip as null when source was null " + "(regression for SGen Collection fallback WriteValueGenerated else-branch null-check)"); Assert.IsTrue(System.Array.IndexOf(bytes, (byte)BinaryTypeCode.PropertySkip) >= 0, "writer must emit PropertySkip marker on the null Items slot " + "(confirms the fix took the PropertySkip path, not an unrelated null-safe code path)"); } [TestMethod] [DataRow(true, true)] [DataRow(true, false)] [DataRow(false, false)] [DataRow(false, true)] public void Serialize_SGenCollectionPropertyNonNull_RoundTripsCorrectly(bool useSgen, bool fastMode) { var model = new SGenNullCollectionParent { Id = 17, Items = new List { new() { Id = 1, Name = "first" }, new() { Id = 2, Name = "second" } }, Note = "positive-collection" }; var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default; options.UseGeneratedCode = useSgen; var bytes = AcBinarySerializer.Serialize(model, options); var roundTrip = AcBinaryDeserializer.Deserialize(bytes, options); Assert.IsNotNull(roundTrip); Assert.AreEqual(model.Id, roundTrip.Id); Assert.AreEqual(model.Note, roundTrip.Note); Assert.IsNotNull(roundTrip.Items, "non-null collection property must round-trip (null-check fix must not break the non-null path)"); Assert.AreEqual(model.Items.Count, roundTrip.Items.Count); Assert.AreEqual(model.Items[0].Id, roundTrip.Items[0].Id); Assert.AreEqual(model.Items[0].Name, roundTrip.Items[0].Name); Assert.AreEqual(model.Items[1].Id, roundTrip.Items[1].Id); Assert.AreEqual(model.Items[1].Name, roundTrip.Items[1].Name); } [TestMethod] [DataRow(true, true)] [DataRow(true, false)] [DataRow(false, false)] [DataRow(false, true)] public void Serialize_SGenDictionaryPropertyNull_DoesNotThrow_AndRoundTripsAsNull(bool useSgen, bool fastMode) { var model = new SGenNullDictionaryParent { Id = 23, Mapping = null!, Note = "regression-dictionary" }; var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default; options.UseGeneratedCode = useSgen; var bytes = AcBinarySerializer.Serialize(model, options); var roundTrip = AcBinaryDeserializer.Deserialize(bytes, options); Assert.IsNotNull(roundTrip); Assert.AreEqual(model.Id, roundTrip.Id); Assert.AreEqual(model.Note, roundTrip.Note); Assert.IsNull(roundTrip.Mapping, "dictionary property must round-trip as null when source was null " + "(pins EmitDirectDictionaryWrite line ~1037 null-check against future regression)"); Assert.IsTrue(System.Array.IndexOf(bytes, (byte)BinaryTypeCode.PropertySkip) >= 0, "writer must emit PropertySkip marker on the null Mapping slot " + "(confirms the PropertySkip path, not an unrelated null-safe code path)"); } [TestMethod] [DataRow(true, true)] [DataRow(true, false)] [DataRow(false, false)] [DataRow(false, true)] public void Serialize_SGenDictionaryPropertyNonNull_RoundTripsCorrectly(bool useSgen, bool fastMode) { var model = new SGenNullDictionaryParent { Id = 29, Mapping = new Dictionary { ["alpha"] = new() { Id = 1, Name = "first" }, ["beta"] = new() { Id = 2, Name = "second" } }, Note = "positive-dictionary" }; var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default; options.UseGeneratedCode = useSgen; var bytes = AcBinarySerializer.Serialize(model, options); var roundTrip = AcBinaryDeserializer.Deserialize(bytes, options); Assert.IsNotNull(roundTrip); Assert.AreEqual(model.Id, roundTrip.Id); Assert.AreEqual(model.Note, roundTrip.Note); Assert.IsNotNull(roundTrip.Mapping, "non-null dictionary property must round-trip (null-check pin must not break the non-null path)"); Assert.AreEqual(model.Mapping.Count, roundTrip.Mapping.Count); Assert.AreEqual(model.Mapping["alpha"].Id, roundTrip.Mapping["alpha"].Id); Assert.AreEqual(model.Mapping["alpha"].Name, roundTrip.Mapping["alpha"].Name); Assert.AreEqual(model.Mapping["beta"].Id, roundTrip.Mapping["beta"].Id); Assert.AreEqual(model.Mapping["beta"].Name, roundTrip.Mapping["beta"].Name); } }