You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by dk...@apache.org on 2018/12/05 16:19:11 UTC

[avro] branch master updated: C# - Generic Record - Fix false positive equality (#204)

This is an automated email from the ASF dual-hosted git repository.

dkulp pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/master by this push:
     new 0530d6c  C# - Generic Record - Fix false positive equality (#204)
0530d6c is described below

commit 0530d6cebf1979ca65b6abb7b2df080460910127
Author: Lukas Sedlacek <ls...@users.noreply.github.com>
AuthorDate: Wed Dec 5 16:19:07 2018 +0000

    C# - Generic Record - Fix false positive equality (#204)
    
    * C# - Test GenericRecord.Equals with [map,array] field
    
    * C# - Fix GenericRecord.Equals for [map,array] field
---
 .../src/apache/main/Generic/GenericRecord.cs       | 56 +++++++++----------
 .../csharp/src/apache/test/Generic/GenericTests.cs | 63 ++++++++++++++++++++++
 2 files changed, 92 insertions(+), 27 deletions(-)

diff --git a/lang/csharp/src/apache/main/Generic/GenericRecord.cs b/lang/csharp/src/apache/main/Generic/GenericRecord.cs
index 3804d15..3550609 100644
--- a/lang/csharp/src/apache/main/Generic/GenericRecord.cs
+++ b/lang/csharp/src/apache/main/Generic/GenericRecord.cs
@@ -16,21 +16,20 @@
  * limitations under the License.
  */
 using System;
+using System.Collections;
 using System.Collections.Generic;
-using System.Linq;
 using System.Text;
-using Avro;
 
 namespace Avro.Generic
 {
     /// <summary>
     /// The default type used by GenericReader and GenericWriter for RecordSchema.
     /// </summary>
-    public class GenericRecord
+    public class GenericRecord : IEquatable<GenericRecord>
     {
         public RecordSchema Schema { get; private set; }
 
-        private IDictionary<string, object> contents = new Dictionary<string, object>();
+        private readonly Dictionary<string, object> contents = new Dictionary<string, object>();
         public GenericRecord(RecordSchema schema)
         {
             this.Schema = schema;
@@ -61,52 +60,55 @@ namespace Avro.Generic
         public override bool Equals(object obj)
         {
             if (this == obj) return true;
-            if (obj != null && obj is GenericRecord)
-            {
-                GenericRecord other = obj as GenericRecord;
-                return Schema.Equals(other.Schema) && areEqual(contents, other.contents);
-            }
-            return false;
+            return obj is GenericRecord
+                && Equals((GenericRecord)obj);
+        }
+
+        public bool Equals(GenericRecord other)
+        {
+            return Schema.Equals(other.Schema)
+                && mapsEqual(contents, other.contents);
         }
 
-        private static bool areEqual(IDictionary<string, object> d1, IDictionary<string, object> d2)
+        private static bool mapsEqual(IDictionary d1, IDictionary d2)
         {
-            if (d1.Count == d2.Count)
+            if (d1.Count != d2.Count) return false;
+
+            foreach (DictionaryEntry kv in d1)
             {
-                foreach (KeyValuePair<string, object> kv in d1)
-                {
-                    object o;
-                    if (!d2.TryGetValue(kv.Key, out o)) return false;
-                    if (!areEqual(o, kv.Value)) return false;
-                }
-                return true;
+                if (!d2.Contains(kv.Key))
+                    return false;
+                if (!objectsEqual(d2[kv.Key], kv.Value))
+                    return false;
             }
-            return false;
+            return true;
         }
 
-        private static bool areEqual(object o1, object o2)
+        private static bool objectsEqual(object o1, object o2)
         {
             if (o1 == null) return o2 == null;
             if (o2 == null) return false;
             if (o1 is Array)
             {
                 if (!(o2 is Array)) return false;
-                return areEqual(o1 as Array, o1 as Array);
+                return arraysEqual((Array)o1 , (Array)o2);
             }
-            else if (o1 is IDictionary<string, object>)
+
+            if (o1 is IDictionary)
             {
-                if (!(o2 is IDictionary<string, object>)) return false;
-                return areEqual(o1 as IDictionary<string, object>, o1 as IDictionary<string, object>);
+                if (!(o2 is IDictionary)) return false;
+                return mapsEqual((IDictionary)o1, (IDictionary)o2);
             }
+
             return o1.Equals(o2);
         }
 
-        private static bool areEqual(Array a1, Array a2)
+        private static bool arraysEqual(Array a1, Array a2)
         {
             if (a1.Length != a2.Length) return false;
             for (int i = 0; i < a1.Length; i++)
             {
-                if (!areEqual(a1.GetValue(i), a2.GetValue(i))) return false;
+                if (!objectsEqual(a1.GetValue(i), a2.GetValue(i))) return false;
             }
             return true;
         }
diff --git a/lang/csharp/src/apache/test/Generic/GenericTests.cs b/lang/csharp/src/apache/test/Generic/GenericTests.cs
index 19951fd..b5bdc8d 100644
--- a/lang/csharp/src/apache/test/Generic/GenericTests.cs
+++ b/lang/csharp/src/apache/test/Generic/GenericTests.cs
@@ -385,6 +385,69 @@ namespace Avro.Test.Generic
             testResolutionMismatch(ws, mkFixed(ws, value), rs);
         }
 
+        [Test]
+        public void TestRecordEquality_arrayFieldnotEqual()
+        {
+            var schema = (RecordSchema)Schema.Parse(
+                "{\"type\":\"record\",\"name\":\"r\",\"fields\":" +
+                "[{\"name\":\"a\",\"type\":{\"type\":\"array\",\"items\":\"int\"}}]}");
+
+            Func<int[], GenericRecord> makeRec = arr => mkRecord(new object[] { "a", arr }, schema);
+
+            var rec1 = makeRec(new[] { 69, 23 });
+            var rec2 = makeRec(new[] { 42, 11 });
+
+            Assert.AreNotEqual(rec1, rec2);
+        }
+
+        [Test]
+        public void TestRecordEquality_arrayFieldequal()
+        {
+            var schema = (RecordSchema)Schema.Parse(
+                "{\"type\":\"record\",\"name\":\"r\",\"fields\":" +
+                "[{\"name\":\"a\",\"type\":{\"type\":\"array\",\"items\":\"int\"}}]}");
+
+            Func<int[], GenericRecord> makeRec = arr => mkRecord(new object[] { "a", arr }, schema);
+
+            // Intentionally duplicated so reference equality doesn't apply
+            var rec1 = makeRec(new[] { 89, 12, 66 });
+            var rec2 = makeRec(new[] { 89, 12, 66 });
+
+            Assert.AreEqual(rec1, rec2);
+        }
+
+        [Test]
+        public void TestRecordEquality_mapFieldequal()
+        {
+            var schema = (RecordSchema)Schema.Parse(
+                "{\"type\":\"record\",\"name\":\"r\",\"fields\":" +
+                "[{\"name\":\"a\",\"type\":{\"type\":\"map\",\"values\":\"int\"}}]}");
+
+            Func<int, GenericRecord> makeRec = value => mkRecord(
+                new object[] { "a", new Dictionary<string, int> { { "key", value } } }, schema);
+
+            var rec1 = makeRec(52);
+            var rec2 = makeRec(52);
+
+            Assert.AreEqual(rec1, rec2);
+        }
+
+        [Test]
+        public void TestRecordEquality_mapFieldnotEqual()
+        {
+            var schema = (RecordSchema)Schema.Parse(
+                "{\"type\":\"record\",\"name\":\"r\",\"fields\":" +
+                "[{\"name\":\"a\",\"type\":{\"type\":\"map\",\"values\":\"int\"}}]}");
+
+            Func<int, GenericRecord> makeRec = value => mkRecord(
+                new object[] { "a", new Dictionary<string, int> { { "key", value } } }, schema);
+
+            var rec1 = makeRec(69);
+            var rec2 = makeRec(98);
+
+            Assert.AreNotEqual(rec1, rec2);
+        }
+
         private static GenericRecord mkRecord(object[] kv, RecordSchema s)
         {
             GenericRecord input = new GenericRecord(s);