THRIFT-5216 generate DeepCopy methods
Client: netstd
Patch: Jens Geyer

This closes #2155
diff --git a/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs b/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
index 1be99b4..061032a 100644
--- a/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
+++ b/lib/netstd/Tests/Thrift.Tests/Collections/TCollectionsTests.cs
@@ -1,4 +1,4 @@
-// Licensed to the Apache Software Foundation(ASF) under one
+// Licensed to the Apache Software Foundation(ASF) under one
 // or more contributor license agreements.See the NOTICE file
 // distributed with this work for additional information
 // regarding copyright ownership.The ASF licenses this file
@@ -17,6 +17,8 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.Xml;
 using System.Text;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Thrift.Collections;
@@ -30,54 +32,209 @@
         //TODO: Add tests for IEnumerable with objects and primitive values inside
 
         [TestMethod]
-        public void TCollection_Equals_Primitive_Test()
+        public void TCollection_List_Equals_Primitive_Test()
         {
             var collection1 = new List<int> {1,2,3};
             var collection2 = new List<int> {1,2,3};
-
-            var result = TCollections.Equals(collection1, collection2);
-
-            Assert.IsTrue(result);
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
         }
 
         [TestMethod]
-        public void TCollection_Equals_Primitive_Different_Test()
+        public void TCollection_List_Equals_Primitive_Different_Test()
         {
             var collection1 = new List<int> { 1, 2, 3 };
             var collection2 = new List<int> { 1, 2 };
+            Assert.IsFalse(TCollections.Equals(collection1, collection2));
+            Assert.IsFalse(collection1.SequenceEqual(collection2));
 
-            var result = TCollections.Equals(collection1, collection2);
-
-            Assert.IsFalse(result);
+            collection2.Add(4);
+            Assert.IsFalse(TCollections.Equals(collection1, collection2));
+            Assert.IsFalse(collection1.SequenceEqual(collection2));
         }
 
         [TestMethod]
-        public void TCollection_Equals_Objects_Test()
+        public void TCollection_List_Equals_Objects_Test()
         {
             var collection1 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
             var collection2 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
-
-            var result = TCollections.Equals(collection1, collection2);
-
-            // references to different collections
-            Assert.IsFalse(result);
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
         }
 
         [TestMethod]
-        public void TCollection_Equals_OneAndTheSameObject_Test()
+        public void TCollection_List_List_Equals_Objects_Test()
+        {
+            var collection1 = new List<List<ExampleClass>> { new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } };
+            var collection2 = new List<List<ExampleClass>> { new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } };
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsFalse(collection1.SequenceEqual(collection2));  // SequenceEqual() calls Equals() of the inner list instead of SequenceEqual()
+        }
+
+        [TestMethod]
+        public void TCollection_List_Equals_OneAndTheSameObject_Test()
         {
             var collection1 = new List<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
             var collection2 = collection1;
-
-            var result = TCollections.Equals(collection1, collection2);
-
-            // references to one and the same collection
-            Assert.IsTrue(result);
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
         }
 
+        [TestMethod]
+        public void TCollection_Set_Equals_Primitive_Test()
+        {
+            var collection1 = new THashSet<int> {1,2,3};
+            var collection2 = new THashSet<int> {1,2,3};
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
+        }
+
+        [TestMethod]
+        public void TCollection_Set_Equals_Primitive_Different_Test()
+        {
+            var collection1 = new THashSet<int> { 1, 2, 3 };
+            var collection2 = new THashSet<int> { 1, 2 };
+            Assert.IsFalse(TCollections.Equals(collection1, collection2));
+            Assert.IsFalse(collection1.SequenceEqual(collection2));
+
+            collection2.Add(4);
+            Assert.IsFalse(TCollections.Equals(collection1, collection2));
+            Assert.IsFalse(collection1.SequenceEqual(collection2));
+        }
+
+        [TestMethod]
+        public void TCollection_Set_Equals_Objects_Test()
+        {
+            var collection1 = new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+            var collection2 = new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
+        }
+
+        [TestMethod]
+        public void TCollection_Set_Set_Equals_Objects_Test()
+        {
+            var collection1 = new THashSet<THashSet<ExampleClass>> { new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } };
+            var collection2 = new THashSet<THashSet<ExampleClass>> { new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } } };
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsFalse(collection1.SequenceEqual(collection2));  // SequenceEqual() calls Equals() of the inner list instead of SequenceEqual()
+        }
+
+        [TestMethod]
+        public void TCollection_Set_Equals_OneAndTheSameObject_Test()
+        {
+            var collection1 = new THashSet<ExampleClass> { new ExampleClass { X = 1 }, new ExampleClass { X = 2 } };
+            var collection2 = collection1;      // references to one and the same collection
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
+        }
+
+
+        [TestMethod]
+        public void TCollection_Map_Equals_Primitive_Test()
+        {
+            var collection1 = new Dictionary<int, int> { [1] = 1, [2] = 2, [3] = 3 };
+            var collection2 = new Dictionary<int, int> { [1] = 1, [2] = 2, [3] = 3 };
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
+        }
+
+        [TestMethod]
+        public void TCollection_Map_Equals_Primitive_Different_Test()
+        {
+            var collection1 = new Dictionary<int, int> { [1] = 1, [2] = 2, [3] = 3 };
+            var collection2 = new Dictionary<int, int> { [1] = 1, [2] = 2 };
+            Assert.IsFalse(TCollections.Equals(collection1, collection2));
+            Assert.IsFalse(collection1.SequenceEqual(collection2));
+
+            collection2[3] = 3;
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
+
+            collection2[3] = 4;
+            Assert.IsFalse(TCollections.Equals(collection1, collection2));
+        }
+
+        [TestMethod]
+        public void TCollection_Map_Equals_Objects_Test()
+        {
+            var collection1 = new Dictionary<int, ExampleClass>
+            {
+                [1] = new ExampleClass { X = 1 },
+                [-1] = new ExampleClass { X = 2 }
+            };
+            var collection2 = new Dictionary<int, ExampleClass>
+            {
+                [1] = new ExampleClass { X = 1 },
+                [-1] = new ExampleClass { X = 2 }
+            };
+
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
+        }
+
+        [TestMethod]
+        public void TCollection_Map_Map_Equals_Objects_Test()
+        {
+            var collection1 = new Dictionary<int, Dictionary<int, ExampleClass>>
+            {
+                [0] = new Dictionary<int, ExampleClass>
+                {
+                    [1] = new ExampleClass { X = 1 },
+                    [-1] = new ExampleClass { X = 2 }
+                }
+            };
+            var collection2 = new Dictionary<int, Dictionary<int, ExampleClass>>
+            {
+                [0] = new Dictionary<int, ExampleClass>
+                {
+                    [1] = new ExampleClass { X = 1 },
+                    [-1] = new ExampleClass { X = 2 }
+                }
+            };
+
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsFalse(collection1.SequenceEqual(collection2));  // SequenceEqual() calls Equals() of the inner list instead of SequenceEqual()
+        }
+
+        [TestMethod]
+        public void TCollection_Map_Equals_OneAndTheSameObject_Test()
+        {
+            var collection1 = new Dictionary<int, ExampleClass>
+            {
+                [1] = new ExampleClass { X = 1 },
+                [-1] = new ExampleClass { X = 2 }
+            };
+            var collection2 = collection1;
+            Assert.IsTrue(TCollections.Equals(collection1, collection2));
+            Assert.IsTrue(collection1.SequenceEqual(collection2));
+        }
+
+
         private class ExampleClass
         {
             public int X { get; set; }
+
+            // all Thrift-generated classes override Equals(), we do just the same
+            public override bool Equals(object that)
+            {
+                if (!(that is ExampleClass other)) return false;
+                if (ReferenceEquals(this, other)) return true;
+
+                return this.X == other.X;
+            }
+
+            //  overriding Equals() requires GetHashCode() as well
+            public override int GetHashCode()
+            {
+                int hashcode = 157;
+                unchecked
+                {
+                    hashcode = (hashcode * 397) + X.GetHashCode();
+                }
+                return hashcode;
+            }
         }
     }
 }
+
diff --git a/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs b/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs
new file mode 100644
index 0000000..f717b4d
--- /dev/null
+++ b/lib/netstd/Tests/Thrift.Tests/DataModel/DeepCopy.cs
@@ -0,0 +1,603 @@
+// Licensed to the Apache Software Foundation(ASF) under one
+// or more contributor license agreements.See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+// 
+//     http://www.apache.org/licenses/LICENSE-2.0
+// 
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OptReqDefTest;
+using Thrift.Collections;
+
+namespace Thrift.Tests.DataModel
+{
+    // ReSharper disable once InconsistentNaming
+    [TestClass]
+    public class DeepCopyTests
+    {
+        [TestMethod]
+        public void Test_Complex_DeepCopy()
+        {
+            var first = InitializeInstance(new RaceDetails());
+            VerifyIdenticalContent(first, InitializeInstance(new RaceDetails()));
+
+            var second = first.DeepCopy();
+            VerifyIdenticalContent(first, second);
+            ModifyInstance(second,0);
+            VerifyDifferentContent(first, second);
+            VerifyIdenticalContent(first, InitializeInstance(new RaceDetails()));
+
+            var third = second.DeepCopy();
+            VerifyIdenticalContent(second, third);
+            ModifyInstance(third,0);
+            VerifyDifferentContent(second, third);
+            VerifyIdenticalContent(first, InitializeInstance(new RaceDetails()));
+        }
+
+        private RaceDetails MakeNestedRaceDetails(int nesting)
+        {
+            if (++nesting > 1)
+                return null;
+
+            var instance = new RaceDetails();
+            InitializeInstance(instance,nesting);
+            return instance;
+        }
+
+        private jack MakeNestedUnion(int nesting)
+        {
+            if (++nesting > 1)
+                return null;
+
+            var details = new RaceDetails();
+            InitializeInstance(details,nesting);
+            return new jack.nested_struct(details);
+        }
+
+
+        private RaceDetails InitializeInstance(RaceDetails instance, int nesting = 0)
+        {
+            // at init, we intentionally leave all non-required fields unset
+            Assert.IsFalse(instance.__isset.opt_one);
+            Assert.IsFalse(instance.__isset.opt_two);
+            Assert.IsFalse(instance.__isset.opt_three);
+            Assert.IsFalse(instance.__isset.opt_four);
+            Assert.IsFalse(instance.__isset.opt_five);
+            Assert.IsFalse(instance.__isset.opt_six);
+            Assert.IsFalse(instance.__isset.opt_seven);
+            Assert.IsFalse(instance.__isset.opt_eight);
+
+            // set all required to null/default
+            instance.Req_one = default;
+            instance.Req_two = default;
+            instance.Req_three = default;
+            instance.Req_four = default;
+            instance.Req_five = default;
+            instance.Req_six = default; 
+            instance.Req_seven = default;;
+            instance.Req_eight = default; 
+
+            // leave non-required fields unset again
+            Assert.IsFalse(instance.__isset.def_one);
+            Assert.IsFalse(instance.__isset.def_two);
+            Assert.IsFalse(instance.__isset.def_three);
+            Assert.IsFalse(instance.__isset.def_four);
+            Assert.IsFalse(instance.__isset.def_five);
+            Assert.IsFalse(instance.__isset.def_six);
+            Assert.IsFalse(instance.__isset.def_seven);
+            Assert.IsFalse(instance.__isset.def_eight);
+
+            // these should have IDL defaults set
+
+            Assert.IsTrue(instance.__isset.opt_one_with_value);
+            Assert.IsTrue(instance.__isset.opt_two_with_value);
+            Assert.IsTrue(instance.__isset.opt_three_with_value);
+            Assert.IsTrue(instance.__isset.opt_four_with_value);
+            Assert.IsTrue(instance.__isset.opt_five_with_value);
+            Assert.IsTrue(instance.__isset.opt_six_with_value);
+            Assert.IsTrue(instance.__isset.opt_seven_with_value);
+            Assert.IsTrue(instance.__isset.opt_eight_with_value);
+
+            Assert.AreEqual(instance.Req_one_with_value, (Distance)1);
+            Assert.AreEqual(instance.Req_two_with_value, 2.22);
+            Assert.AreEqual(instance.Req_three_with_value, 3);
+            Assert.AreEqual(instance.Req_four_with_value, "four");
+            Assert.AreEqual("five", Encoding.UTF8.GetString(instance.Req_five_with_value));
+
+            Assert.IsTrue(instance.Req_six_with_value.Count == 1);
+            Assert.AreEqual(instance.Req_six_with_value[0], 6 );
+
+            Assert.IsTrue(instance.Req_seven_with_value.Count == 1);
+            Assert.IsTrue(instance.Req_seven_with_value.Contains(7));
+
+            Assert.IsTrue(instance.Req_eight_with_value.Count == 1);
+            Assert.IsTrue(instance.Req_eight_with_value[8] == 8);
+
+            Assert.IsTrue(instance.__isset.def_one_with_value);
+            Assert.IsTrue(instance.__isset.def_two_with_value);
+            Assert.IsTrue(instance.__isset.def_three_with_value);
+            Assert.IsTrue(instance.__isset.def_four_with_value);
+            Assert.IsTrue(instance.__isset.def_five_with_value);
+            Assert.IsTrue(instance.__isset.def_six_with_value);
+            Assert.IsTrue(instance.__isset.def_seven_with_value);
+            Assert.IsTrue(instance.__isset.def_eight_with_value);
+
+            instance.Last_of_the_mohicans = true;
+
+            if (nesting < 2)
+            {
+                instance.Far_list = new List<Distance>() { Distance.foo, Distance.bar, Distance.baz };
+                instance.Far_set = new THashSet<Distance>() { Distance.foo, Distance.bar, Distance.baz };
+                instance.Far_map = new Dictionary<Distance, Distance>() { [Distance.foo] = Distance.foo, [Distance.bar] = Distance.bar, [Distance.baz] = Distance.baz };
+
+                instance.Far_set_list = new THashSet<List<Distance>>() { new List<Distance>() { Distance.foo } };
+                instance.Far_list_map_set = new List<Dictionary<sbyte, THashSet<Distance>>>() { new Dictionary<sbyte, THashSet<Distance>>() { [1] = new THashSet<Distance>() { Distance.baz } } };
+                instance.Far_map_dist_to_rds = new Dictionary<Distance, List<RaceDetails>>() { [Distance.bar] = new List<RaceDetails>() { MakeNestedRaceDetails(nesting) } };
+
+                instance.Req_nested = MakeNestedRaceDetails(nesting);
+                Assert.IsFalse(instance.__isset.opt_nested);
+                Assert.IsFalse(instance.__isset.def_nested);
+
+                instance.Req_union = MakeNestedUnion(nesting);
+                Assert.IsFalse(instance.__isset.opt_union);
+                Assert.IsFalse(instance.__isset.def_union);
+            }
+
+            instance.Triplesix = (Distance)666;
+
+            return instance;
+        }
+
+        private void ModifyInstance(RaceDetails instance, int level)
+        {
+            if ((instance == null) || (++level > 4))
+                return;
+
+            instance.Opt_one = ModifyValue(instance.Opt_one);
+            instance.Opt_two = ModifyValue(instance.Opt_two);
+            instance.Opt_three = ModifyValue(instance.Opt_three);
+            instance.Opt_four = ModifyValue(instance.Opt_four);
+            instance.Opt_five = ModifyValue(instance.Opt_five);
+            instance.Opt_six = ModifyValue(instance.Opt_six);
+            instance.Opt_seven = ModifyValue(instance.Opt_seven);
+            instance.Opt_eight = ModifyValue(instance.Opt_eight);
+
+            instance.Req_one = ModifyValue(instance.Req_one);
+            instance.Req_two = ModifyValue(instance.Req_two);
+            instance.Req_three = ModifyValue(instance.Req_three);
+            instance.Req_four = ModifyValue(instance.Req_four);
+            instance.Req_five = ModifyValue(instance.Req_five);
+            instance.Req_six = ModifyValue(instance.Req_six);
+            instance.Req_seven = ModifyValue(instance.Req_seven);
+            instance.Req_eight = ModifyValue(instance.Req_eight);
+
+            instance.Def_one = ModifyValue(instance.Def_one);
+            instance.Def_two = ModifyValue(instance.Def_two);
+            instance.Def_three = ModifyValue(instance.Def_three);
+            instance.Def_four = ModifyValue(instance.Def_four);
+            instance.Def_five = ModifyValue(instance.Def_five);
+            instance.Def_six = ModifyValue(instance.Def_six);
+            instance.Def_seven = ModifyValue(instance.Def_seven);
+            instance.Def_eight = ModifyValue(instance.Def_eight);
+
+            instance.Opt_one_with_value = ModifyValue(instance.Opt_one_with_value);
+            instance.Opt_two_with_value = ModifyValue(instance.Opt_two_with_value);
+            instance.Opt_three_with_value = ModifyValue(instance.Opt_three_with_value);
+            instance.Opt_four_with_value = ModifyValue(instance.Opt_four_with_value);
+            instance.Opt_five_with_value = ModifyValue(instance.Opt_five_with_value);
+            instance.Opt_six_with_value = ModifyValue(instance.Opt_six_with_value);
+            instance.Opt_seven_with_value = ModifyValue(instance.Opt_seven_with_value);
+            instance.Opt_eight_with_value = ModifyValue(instance.Opt_eight_with_value);
+
+            instance.Req_one_with_value = ModifyValue(instance.Req_one_with_value);
+            instance.Req_two_with_value = ModifyValue(instance.Req_two_with_value);
+            instance.Req_three_with_value = ModifyValue(instance.Req_three_with_value);
+            instance.Req_four_with_value = ModifyValue(instance.Req_four_with_value);
+            instance.Req_five_with_value = ModifyValue(instance.Req_five_with_value);
+            instance.Req_six_with_value = ModifyValue(instance.Req_six_with_value);
+            instance.Req_seven_with_value = ModifyValue(instance.Req_seven_with_value);
+            instance.Req_eight_with_value = ModifyValue(instance.Req_eight_with_value);
+
+            instance.Def_one_with_value = ModifyValue(instance.Def_one_with_value);
+            instance.Def_two_with_value = ModifyValue(instance.Def_two_with_value);
+            instance.Def_three_with_value = ModifyValue(instance.Def_three_with_value);
+            instance.Def_four_with_value = ModifyValue(instance.Def_four_with_value);
+            instance.Def_five_with_value = ModifyValue(instance.Def_five_with_value);
+            instance.Def_six_with_value = ModifyValue(instance.Def_six_with_value);
+            instance.Def_seven_with_value = ModifyValue(instance.Def_seven_with_value);
+            instance.Def_eight_with_value = ModifyValue(instance.Def_eight_with_value);
+
+            instance.Last_of_the_mohicans = ModifyValue(instance.Last_of_the_mohicans);
+
+            instance.Far_list = ModifyValue(instance.Far_list);
+            instance.Far_set = ModifyValue(instance.Far_set);
+            instance.Far_map = ModifyValue(instance.Far_map);
+
+            instance.Far_set_list = ModifyValue(instance.Far_set_list);
+            instance.Far_list_map_set = ModifyValue(instance.Far_list_map_set);
+            instance.Far_map_dist_to_rds = ModifyValue(instance.Far_map_dist_to_rds, level);
+
+            instance.Req_nested = ModifyValue(instance.Req_nested, level);
+            instance.Opt_nested = ModifyValue(instance.Opt_nested, level);
+            instance.Def_nested = ModifyValue(instance.Def_nested, level);
+
+            instance.Req_union = ModifyValue(instance.Req_union, level);
+            instance.Opt_union = ModifyValue(instance.Opt_union, level);
+            instance.Def_union = ModifyValue(instance.Def_union, level);
+
+            instance.Triplesix = ModifyValue(instance.Triplesix);
+        }
+
+        private jack ModifyValue(jack value, int level)
+        {
+            if (++level > 4)
+                return value;
+
+            if (value == null)
+                value = MakeNestedUnion(0);
+            Debug.Assert(value.As_nested_struct != null);
+            ModifyInstance(value.As_nested_struct, level);
+            return value;
+        }
+
+        private RaceDetails ModifyValue(RaceDetails value, int level)
+        {
+            if (++level > 4)
+                return value;
+
+            if (value == null)
+                value = new RaceDetails();
+            ModifyInstance(value,level);
+            return value;
+        }
+
+        private Dictionary<Distance, List<RaceDetails>> ModifyValue(Dictionary<Distance, List<RaceDetails>> value, int level)
+        {
+            if (value == null)
+                value = new Dictionary<Distance, List<RaceDetails>>();
+
+            if (++level > 4)
+                return value;
+
+            var details = new RaceDetails();
+            InitializeInstance(details);
+            value[Distance.foo] = new List<RaceDetails>() { details };
+
+            if (value.TryGetValue(Distance.bar, out var list) && (list.Count > 0))
+            {
+                ModifyInstance(list[0], level);
+                list.Add(null);
+            }
+
+            value[Distance.baz] = null;
+
+            return value;
+        }
+
+        private List<Dictionary<sbyte, THashSet<Distance>>> ModifyValue(List<Dictionary<sbyte, THashSet<Distance>>> value)
+        {
+            if (value == null)
+                value = new List<Dictionary<sbyte, THashSet<Distance>>>();
+
+            if (value.Count == 0)
+                value.Add(new Dictionary<sbyte, THashSet<Distance>>());
+            else
+                value.Add(null);
+
+            sbyte key = (sbyte)(value[0].Count + 10);
+            if (value[0].Count == 0)
+                value[0].Add(key, new THashSet<Distance>());
+            else
+                value[0].Add(key, null);
+
+            foreach (var entry in value)
+            {
+                if (entry != null)
+                {
+                    foreach (var pair in entry)
+                    {
+                        if (pair.Value != null)
+                        {
+                            if (pair.Value.Contains(Distance.baz))
+                                pair.Value.Remove(Distance.baz);
+                            else
+                                pair.Value.Add(Distance.baz);
+                        }
+                    }
+                }
+            }
+
+            return value;
+        }
+
+        private THashSet<List<Distance>> ModifyValue(THashSet<List<Distance>> value)
+        {
+            if (value == null)
+                value = new THashSet<List<Distance>>();
+
+            if (value.Count == 0)
+                value.Add(new List<Distance>());
+            else
+                value.Add(null);
+
+            foreach (var entry in value)
+                if( entry != null)
+                    entry.Add(Distance.baz);
+
+            return value;
+        }
+
+        private Dictionary<Distance, Distance> ModifyValue(Dictionary<Distance, Distance> value)
+        {
+            if (value == null)
+                value = new Dictionary<Distance, Distance>();
+            value[Distance.foo] = value.ContainsKey(Distance.foo) ? ++value[Distance.foo] : Distance.foo;
+            value[Distance.bar] = value.ContainsKey(Distance.bar) ? ++value[Distance.bar] : Distance.bar;
+            value[Distance.baz] = value.ContainsKey(Distance.baz) ? ++value[Distance.baz] : Distance.baz;
+            return value;
+        }
+
+        private THashSet<Distance> ModifyValue(THashSet<Distance> value)
+        {
+            if (value == null)
+                value = new THashSet<Distance>();
+
+            if (value.Contains(Distance.foo))
+                value.Remove(Distance.foo);
+            else
+                value.Add(Distance.foo);
+
+            if (value.Contains(Distance.bar))
+                value.Remove(Distance.bar);
+            else
+                value.Add(Distance.bar);
+
+            if (value.Contains(Distance.baz))
+                value.Remove(Distance.baz);
+            else
+                value.Add(Distance.baz);
+
+            return value;
+        }
+
+        private List<Distance> ModifyValue(List<Distance> value)
+        {
+            if (value == null)
+                value = new List<Distance>();
+            value.Add(Distance.foo);
+            value.Add(Distance.bar);
+            value.Add(Distance.baz);
+            return value;
+        }
+
+        private bool ModifyValue(bool value)
+        {
+            return !value;
+        }
+
+        private Dictionary<sbyte, short> ModifyValue(Dictionary<sbyte, short> value)
+        {
+            if (value == null)
+                value = new Dictionary<sbyte, short>();
+            value.Add((sbyte)(value.Count + 10), (short)value.Count);
+            return value;
+        }
+
+        private THashSet<long> ModifyValue(THashSet<long> value)
+        {
+            if (value == null)
+                value = new THashSet<long>();
+            value.Add(value.Count+100);
+            return value;
+        }
+
+        private List<int> ModifyValue(List<int> value)
+        {
+            if (value == null)
+                value = new List<int>();
+            value.Add(value.Count);
+            return value;
+        }
+
+        private byte[] ModifyValue(byte[] value)
+        {
+            if (value == null)
+                value = new byte[1] { 0 };
+            if (value.Length > 0)
+                value[0] = (value[0] < 0xFF) ? ++value[0] : (byte)0;
+            return value;
+        }
+
+        private string ModifyValue(string value)
+        {
+            return value + "1";
+        }
+
+        private double ModifyValue(double value)
+        {
+            return value + 1.1;
+        }
+
+        private short ModifyValue(short value)
+        {
+            return ++value;
+        }
+
+        private Distance ModifyValue(Distance value)
+        {
+            return ++value;
+        }
+
+        private void VerifyDifferentContent(RaceDetails first, RaceDetails second)
+        {
+            Assert.AreNotEqual(first, second);
+
+            Assert.AreNotEqual(first.Opt_two, second.Opt_two);
+            Assert.AreNotEqual(first.Opt_three, second.Opt_three);
+            Assert.AreNotEqual(first.Opt_four, second.Opt_four);
+            Assert.IsFalse(TCollections.Equals(first.Opt_five, second.Opt_five));
+            Assert.IsFalse(TCollections.Equals(first.Opt_six, second.Opt_six));
+            Assert.IsFalse(TCollections.Equals(first.Opt_seven, second.Opt_seven));
+            Assert.IsFalse(TCollections.Equals(first.Opt_eight, second.Opt_eight));
+
+            Assert.AreNotEqual(first.Req_one, second.Req_one);
+            Assert.AreNotEqual(first.Req_two, second.Req_two);
+            Assert.AreNotEqual(first.Req_three, second.Req_three);
+            Assert.AreNotEqual(first.Req_four, second.Req_four);
+            Assert.IsFalse(TCollections.Equals(first.Req_five, second.Req_five));
+            Assert.IsFalse(TCollections.Equals(first.Req_six, second.Req_six));
+            Assert.IsFalse(TCollections.Equals(first.Req_seven, second.Req_seven));
+            Assert.IsFalse(TCollections.Equals(first.Req_eight, second.Req_eight));
+
+            Assert.AreNotEqual(first.Def_one, second.Def_one);
+            Assert.AreNotEqual(first.Def_two, second.Def_two);
+            Assert.AreNotEqual(first.Def_three, second.Def_three);
+            Assert.AreNotEqual(first.Def_four, second.Def_four);
+            Assert.IsFalse(TCollections.Equals(first.Def_five, second.Def_five));
+            Assert.IsFalse(TCollections.Equals(first.Def_six, second.Def_six));
+            Assert.IsFalse(TCollections.Equals(first.Def_seven, second.Def_seven));
+            Assert.IsFalse(TCollections.Equals(first.Def_eight, second.Def_eight));
+
+            Assert.AreNotEqual(first.Opt_one_with_value, second.Opt_one_with_value);
+            Assert.AreNotEqual(first.Opt_two_with_value, second.Opt_two_with_value);
+            Assert.AreNotEqual(first.Opt_three_with_value, second.Opt_three_with_value);
+            Assert.AreNotEqual(first.Opt_four_with_value, second.Opt_four_with_value);
+            Assert.IsFalse(TCollections.Equals(first.Opt_five_with_value, second.Opt_five_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Opt_six_with_value, second.Opt_six_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Opt_seven_with_value, second.Opt_seven_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Opt_eight_with_value, second.Opt_eight_with_value));
+
+            Assert.AreNotEqual(first.Req_one_with_value, second.Req_one_with_value);
+            Assert.AreNotEqual(first.Req_two_with_value, second.Req_two_with_value);
+            Assert.AreNotEqual(first.Req_three_with_value, second.Req_three_with_value);
+            Assert.AreNotEqual(first.Req_four_with_value, second.Req_four_with_value);
+            Assert.IsFalse(TCollections.Equals(first.Req_five_with_value, second.Req_five_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Req_six_with_value, second.Req_six_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Req_seven_with_value, second.Req_seven_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Req_eight_with_value, second.Req_eight_with_value));
+
+            Assert.AreNotEqual(first.Def_one_with_value, second.Def_one_with_value);
+            Assert.AreNotEqual(first.Def_two_with_value, second.Def_two_with_value);
+            Assert.AreNotEqual(first.Def_three_with_value, second.Def_three_with_value);
+            Assert.AreNotEqual(first.Def_four_with_value, second.Def_four_with_value);
+            Assert.IsFalse(TCollections.Equals(first.Def_five_with_value, second.Def_five_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Def_six_with_value, second.Def_six_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Def_seven_with_value, second.Def_seven_with_value));
+            Assert.IsFalse(TCollections.Equals(first.Def_eight_with_value, second.Def_eight_with_value));
+
+            Assert.AreNotEqual(first.Last_of_the_mohicans, second.Last_of_the_mohicans);
+
+            Assert.IsFalse(TCollections.Equals(first.Far_list, second.Far_list));
+            Assert.IsFalse(TCollections.Equals(first.Far_set, second.Far_set));
+            Assert.IsFalse(TCollections.Equals(first.Far_map, second.Far_map));
+
+            Assert.IsFalse(TCollections.Equals(first.Far_set_list, second.Far_set_list));
+            Assert.IsFalse(TCollections.Equals(first.Far_list_map_set, second.Far_list_map_set));
+            Assert.IsFalse(TCollections.Equals(first.Far_map_dist_to_rds, second.Far_map_dist_to_rds));
+
+            Assert.AreNotEqual(first.Req_nested, second.Req_nested);
+            Assert.AreNotEqual(first.Opt_nested, second.Opt_nested);
+            Assert.AreNotEqual(first.Def_nested, second.Def_nested);
+
+            Assert.AreNotEqual(first.Req_union, second.Req_union);
+            Assert.AreNotEqual(first.Opt_union, second.Opt_union);
+            Assert.AreNotEqual(first.Def_union, second.Def_union);
+
+            Assert.AreNotEqual(first.Triplesix, second.Triplesix);
+        }
+
+        private void VerifyIdenticalContent(RaceDetails first, RaceDetails second)
+        {
+            Assert.AreEqual(first, second);
+
+            Assert.AreEqual(first.Opt_two, second.Opt_two);
+            Assert.AreEqual(first.Opt_three, second.Opt_three);
+            Assert.AreEqual(first.Opt_four, second.Opt_four);
+            Assert.IsTrue(TCollections.Equals(first.Opt_five, second.Opt_five));
+            Assert.IsTrue(TCollections.Equals(first.Opt_six, second.Opt_six));
+            Assert.IsTrue(TCollections.Equals(first.Opt_seven, second.Opt_seven));
+            Assert.IsTrue(TCollections.Equals(first.Opt_eight, second.Opt_eight));
+
+            Assert.AreEqual(first.Req_one, second.Req_one);
+            Assert.AreEqual(first.Req_two, second.Req_two);
+            Assert.AreEqual(first.Req_three, second.Req_three);
+            Assert.AreEqual(first.Req_four, second.Req_four);
+            Assert.IsTrue(TCollections.Equals(first.Req_five, second.Req_five));
+            Assert.IsTrue(TCollections.Equals(first.Req_six, second.Req_six));
+            Assert.IsTrue(TCollections.Equals(first.Req_seven, second.Req_seven));
+            Assert.IsTrue(TCollections.Equals(first.Req_eight, second.Req_eight));
+
+            Assert.AreEqual(first.Def_one, second.Def_one);
+            Assert.AreEqual(first.Def_two, second.Def_two);
+            Assert.AreEqual(first.Def_three, second.Def_three);
+            Assert.AreEqual(first.Def_four, second.Def_four);
+            Assert.IsTrue(TCollections.Equals(first.Def_five, second.Def_five));
+            Assert.IsTrue(TCollections.Equals(first.Def_six, second.Def_six));
+            Assert.IsTrue(TCollections.Equals(first.Def_seven, second.Def_seven));
+            Assert.IsTrue(TCollections.Equals(first.Def_eight, second.Def_eight));
+
+            Assert.AreEqual(first.Opt_one_with_value, second.Opt_one_with_value);
+            Assert.AreEqual(first.Opt_two_with_value, second.Opt_two_with_value);
+            Assert.AreEqual(first.Opt_three_with_value, second.Opt_three_with_value);
+            Assert.AreEqual(first.Opt_four_with_value, second.Opt_four_with_value);
+            Assert.IsTrue(TCollections.Equals(first.Opt_five_with_value, second.Opt_five_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Opt_six_with_value, second.Opt_six_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Opt_seven_with_value, second.Opt_seven_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Opt_eight_with_value, second.Opt_eight_with_value));
+
+            Assert.AreEqual(first.Req_one_with_value, second.Req_one_with_value);
+            Assert.AreEqual(first.Req_two_with_value, second.Req_two_with_value);
+            Assert.AreEqual(first.Req_three_with_value, second.Req_three_with_value);
+            Assert.AreEqual(first.Req_four_with_value, second.Req_four_with_value);
+            Assert.IsTrue(TCollections.Equals(first.Req_five_with_value, second.Req_five_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Req_six_with_value, second.Req_six_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Req_seven_with_value, second.Req_seven_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Req_eight_with_value, second.Req_eight_with_value));
+
+            Assert.AreEqual(first.Def_one_with_value, second.Def_one_with_value);
+            Assert.AreEqual(first.Def_two_with_value, second.Def_two_with_value);
+            Assert.AreEqual(first.Def_three_with_value, second.Def_three_with_value);
+            Assert.AreEqual(first.Def_four_with_value, second.Def_four_with_value);
+            Assert.IsTrue(TCollections.Equals(first.Def_five_with_value, second.Def_five_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Def_six_with_value, second.Def_six_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Def_seven_with_value, second.Def_seven_with_value));
+            Assert.IsTrue(TCollections.Equals(first.Def_eight_with_value, second.Def_eight_with_value));
+
+            Assert.AreEqual(first.Last_of_the_mohicans, second.Last_of_the_mohicans);
+
+            Assert.IsTrue(TCollections.Equals(first.Far_list, second.Far_list));
+            Assert.IsTrue(TCollections.Equals(first.Far_set, second.Far_set));
+            Assert.IsTrue(TCollections.Equals(first.Far_map, second.Far_map));
+
+            Assert.IsTrue(TCollections.Equals(first.Far_set_list, second.Far_set_list));
+            Assert.IsTrue(TCollections.Equals(first.Far_list_map_set, second.Far_list_map_set));
+            Assert.IsTrue(TCollections.Equals(first.Far_map_dist_to_rds, second.Far_map_dist_to_rds));
+
+            Assert.AreEqual(first.Req_nested, second.Req_nested);
+            Assert.AreEqual(first.Opt_nested, second.Opt_nested);
+            Assert.AreEqual(first.Def_nested, second.Def_nested);
+
+            Assert.AreEqual(first.Req_union, second.Req_union);
+            Assert.AreEqual(first.Opt_union, second.Opt_union);
+            Assert.AreEqual(first.Def_union, second.Def_union);
+
+            Assert.AreEqual(first.Triplesix, second.Triplesix);
+        }
+
+    }
+}
diff --git a/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj b/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj
index 20fdfe4..4b39e7d 100644
--- a/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj
+++ b/lib/netstd/Tests/Thrift.Tests/Thrift.Tests.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <!--
     Licensed to the Apache Software Foundation(ASF) under one
     or more contributor license agreements.See the NOTICE file
@@ -29,6 +29,7 @@
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\..\Thrift\Thrift.csproj" />
+    <ProjectReference Include="..\Thrift.PublicInterfaces.Compile.Tests\Thrift.PublicInterfaces.Compile.Tests.csproj" />
   </ItemGroup>
   <ItemGroup>
     <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />