From 1a7b9964bc2139af31f6fb3e88fb5bdccac211ae Mon Sep 17 00:00:00 2001 From: Rian Stockbower Date: Fri, 28 Apr 2017 11:26:11 -0400 Subject: [PATCH] Journals, Todos, Events have symmetrical GetHashCode and Equals methods #271 --- .../EqualityAndHashingTests.cs | 43 +++++++++++++---- .../Ical.Net.UnitTests.csproj | 1 + .../Ical.Net/Components/CalendarEvent.cs | 7 ++- .../Ical.Net/Ical.Net/Components/Journal.cs | 17 ++++++- .../Ical.Net/Components/RecurringComponent.cs | 47 +++++++++++++++++++ .../EqualityAndHashingTests.cs | 43 +++++++++++++---- .../Ical.Net.UnitTests.csproj | 3 ++ v2/ical.NET.UnitTests/packages.config | 1 + v2/ical.NET/Components/Event.cs | 6 +-- v2/ical.NET/Components/Journal.cs | 17 ++++++- v2/ical.NET/Components/RecurringComponent.cs | 47 +++++++++++++++++++ 11 files changed, 203 insertions(+), 29 deletions(-) diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/EqualityAndHashingTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/EqualityAndHashingTests.cs index 34b0ec3d9..7379d99af 100644 --- a/net-core/Ical.Net/Ical.Net.UnitTests/EqualityAndHashingTests.cs +++ b/net-core/Ical.Net/Ical.Net.UnitTests/EqualityAndHashingTests.cs @@ -257,23 +257,46 @@ public void Resources_Tests() Assert.IsFalse(serialized.Contains("Hello")); } - [Test] - public void Attachment_Tests() + internal static (byte[] original, byte[] copy) GetAttachments() { var payload = Encoding.UTF8.GetBytes("This is an attachment!"); var payloadCopy = new byte[payload.Length]; Array.Copy(payload, payloadCopy, payload.Length); + return (payload, payloadCopy); + } + + [Test, TestCaseSource(nameof(RecurringComponentAttachment_TestCases))] + public void RecurringComponentAttachmentTests(RecurringComponent noAttachment, RecurringComponent withAttachment) + { + var attachments = GetAttachments(); - var withAttachment = GetSimpleEvent(); - withAttachment.Attachments.Add(new Attachment(payload)); + Assert.AreNotEqual(noAttachment, withAttachment); + Assert.AreNotEqual(noAttachment.GetHashCode(), withAttachment.GetHashCode()); - var noAttachment = GetSimpleEvent(); - Assert.AreNotEqual(withAttachment, noAttachment); - Assert.AreNotEqual(withAttachment.GetHashCode(), noAttachment.GetHashCode()); + noAttachment.Attachments.Add(new Attachment(attachments.copy)); - noAttachment.Attachments.Add(new Attachment(payloadCopy)); - Assert.AreEqual(withAttachment, noAttachment); - Assert.AreEqual(withAttachment.GetHashCode(), noAttachment.GetHashCode()); + Assert.AreEqual(noAttachment, withAttachment); + Assert.AreEqual(noAttachment.GetHashCode(), withAttachment.GetHashCode()); + } + + public static IEnumerable RecurringComponentAttachment_TestCases() + { + var attachments = GetAttachments(); + + var journalNoAttach = new Journal { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + var journalWithAttach = new Journal { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + journalWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(journalNoAttach, journalWithAttach).SetName("Journal recurring component attachment"); + + var todoNoAttach = new Todo { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + var todoWithAttach = new Todo { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + todoWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(todoNoAttach, todoWithAttach).SetName("Todo recurring component attachment"); + + var eventNoAttach = GetSimpleEvent(); + var eventWithAttach = GetSimpleEvent(); + eventWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(eventNoAttach, eventWithAttach).SetName("Event recurring component attachment"); } } } diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Ical.Net.UnitTests.csproj b/net-core/Ical.Net/Ical.Net.UnitTests/Ical.Net.UnitTests.csproj index 03315e030..1f9f220e6 100644 --- a/net-core/Ical.Net/Ical.Net.UnitTests/Ical.Net.UnitTests.csproj +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Ical.Net.UnitTests.csproj @@ -7,6 +7,7 @@ + diff --git a/net-core/Ical.Net/Ical.Net/Components/CalendarEvent.cs b/net-core/Ical.Net/Ical.Net/Components/CalendarEvent.cs index 1e4c276c9..4702a1aaf 100644 --- a/net-core/Ical.Net/Ical.Net/Components/CalendarEvent.cs +++ b/net-core/Ical.Net/Ical.Net/Components/CalendarEvent.cs @@ -298,8 +298,8 @@ protected bool Equals(CalendarEvent other) && Attachments.SequenceEqual(other.Attachments) && CollectionHelpers.Equals(ExceptionDates, other.ExceptionDates) && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) - && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, true) - && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, true); + && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, orderSignificant: true) + && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, orderSignificant: true); return result; } @@ -326,12 +326,11 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Resources); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDates); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); return hashCode; } } - public int CompareTo(CalendarEvent other) { if (DtStart.Equals(other.DtStart)) diff --git a/net-core/Ical.Net/Ical.Net/Components/Journal.cs b/net-core/Ical.Net/Ical.Net/Components/Journal.cs index da2b6cce0..d1b73a0bd 100644 --- a/net-core/Ical.Net/Ical.Net/Components/Journal.cs +++ b/net-core/Ical.Net/Ical.Net/Components/Journal.cs @@ -23,8 +23,23 @@ private void Initialize() protected override void OnDeserializing(StreamingContext context) { base.OnDeserializing(context); - Initialize(); } + + protected bool Equals(Journal other) => Start.Equals(other.Start) && Equals(other as RecurringComponent); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((Journal)obj); + } + + public override int GetHashCode() + { + var hashCode = Start?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ base.GetHashCode(); + return hashCode; + } } } \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/RecurringComponent.cs b/net-core/Ical.Net/Ical.Net/Components/RecurringComponent.cs index dc5362cd5..e82a83a60 100644 --- a/net-core/Ical.Net/Ical.Net/Components/RecurringComponent.cs +++ b/net-core/Ical.Net/Ical.Net/Components/RecurringComponent.cs @@ -226,5 +226,52 @@ public virtual IList PollAlarms(IDateTime startTime, IDateTime } return occurrences; } + + protected bool Equals(RecurringComponent other) + { + var result = Equals(DtStart, other.DtStart) + && Equals(Priority, other.Priority) + && string.Equals(Summary, other.Summary, StringComparison.OrdinalIgnoreCase) + && string.Equals(Class, other.Class, StringComparison.OrdinalIgnoreCase) + && string.Equals(Description, other.Description, StringComparison.OrdinalIgnoreCase) + && Equals(RecurrenceId, other.RecurrenceId) + && Attachments.SequenceEqual(other.Attachments) + && CollectionHelpers.Equals(Categories, other.Categories) + && CollectionHelpers.Equals(Contacts, other.Contacts) + && CollectionHelpers.Equals(ExceptionDates, other.ExceptionDates) + && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) + && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, orderSignificant: true) + && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, orderSignificant: true); + + return result; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((RecurringComponent)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = DtStart?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ Priority.GetHashCode(); + hashCode = (hashCode * 397) ^ (Summary?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Class?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Description?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (RecurrenceId?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Attachments); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Categories); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Contacts); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); + return hashCode; + } + } } } \ No newline at end of file diff --git a/v2/ical.NET.UnitTests/EqualityAndHashingTests.cs b/v2/ical.NET.UnitTests/EqualityAndHashingTests.cs index 024c82fe8..062161406 100644 --- a/v2/ical.NET.UnitTests/EqualityAndHashingTests.cs +++ b/v2/ical.NET.UnitTests/EqualityAndHashingTests.cs @@ -257,23 +257,46 @@ public void Resources_Tests() Assert.IsFalse(serialized.Contains("Hello")); } - [Test] - public void Attachment_Tests() + internal static (byte[] original, byte[] copy) GetAttachments() { var payload = Encoding.UTF8.GetBytes("This is an attachment!"); var payloadCopy = new byte[payload.Length]; Array.Copy(payload, payloadCopy, payload.Length); + return (payload, payloadCopy); + } + + [Test, TestCaseSource(nameof(RecurringComponentAttachment_TestCases))] + public void RecurringComponentAttachmentTests(RecurringComponent noAttachment, RecurringComponent withAttachment) + { + var attachments = GetAttachments(); - var withAttachment = GetSimpleEvent(); - withAttachment.Attachments.Add(new Attachment(payload)); + Assert.AreNotEqual(noAttachment, withAttachment); + Assert.AreNotEqual(noAttachment.GetHashCode(), withAttachment.GetHashCode()); - var noAttachment = GetSimpleEvent(); - Assert.AreNotEqual(withAttachment, noAttachment); - Assert.AreNotEqual(withAttachment.GetHashCode(), noAttachment.GetHashCode()); + noAttachment.Attachments.Add(new Attachment(attachments.copy)); - noAttachment.Attachments.Add(new Attachment(payloadCopy)); - Assert.AreEqual(withAttachment, noAttachment); - Assert.AreEqual(withAttachment.GetHashCode(), noAttachment.GetHashCode()); + Assert.AreEqual(noAttachment, withAttachment); + Assert.AreEqual(noAttachment.GetHashCode(), withAttachment.GetHashCode()); + } + + public static IEnumerable RecurringComponentAttachment_TestCases() + { + var attachments = GetAttachments(); + + var journalNoAttach = new Journal {Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!"}; + var journalWithAttach = new Journal {Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!"}; + journalWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(journalNoAttach, journalWithAttach).SetName("Journal recurring component attachment"); + + var todoNoAttach = new Todo { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + var todoWithAttach = new Todo { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!"}; + todoWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(todoNoAttach, todoWithAttach).SetName("Todo recurring component attachment"); + + var eventNoAttach = GetSimpleEvent(); + var eventWithAttach = GetSimpleEvent(); + eventWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(eventNoAttach, eventWithAttach).SetName("Event recurring component attachment"); } } } diff --git a/v2/ical.NET.UnitTests/Ical.Net.UnitTests.csproj b/v2/ical.NET.UnitTests/Ical.Net.UnitTests.csproj index a374442c1..536556a95 100644 --- a/v2/ical.NET.UnitTests/Ical.Net.UnitTests.csproj +++ b/v2/ical.NET.UnitTests/Ical.Net.UnitTests.csproj @@ -69,6 +69,9 @@ 3.5 + + ..\packages\System.ValueTuple.4.3.0\lib\portable-net40+sl4+win8+wp8\System.ValueTuple.dll + diff --git a/v2/ical.NET.UnitTests/packages.config b/v2/ical.NET.UnitTests/packages.config index 0d447ca8f..07b215c91 100644 --- a/v2/ical.NET.UnitTests/packages.config +++ b/v2/ical.NET.UnitTests/packages.config @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/v2/ical.NET/Components/Event.cs b/v2/ical.NET/Components/Event.cs index 8e6cd3c1a..16842a8f5 100644 --- a/v2/ical.NET/Components/Event.cs +++ b/v2/ical.NET/Components/Event.cs @@ -297,8 +297,8 @@ protected bool Equals(Event other) && Attachments.SequenceEqual(other.Attachments) && CollectionHelpers.Equals(ExceptionDates, other.ExceptionDates) && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) - && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, true) - && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, true); + && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, orderSignificant: true) + && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, orderSignificant: true); return result; } @@ -325,8 +325,8 @@ public override int GetHashCode() hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Resources); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDates); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); return hashCode; } } diff --git a/v2/ical.NET/Components/Journal.cs b/v2/ical.NET/Components/Journal.cs index c1ede9626..7b37bb949 100644 --- a/v2/ical.NET/Components/Journal.cs +++ b/v2/ical.NET/Components/Journal.cs @@ -24,8 +24,23 @@ private void Initialize() protected override void OnDeserializing(StreamingContext context) { base.OnDeserializing(context); - Initialize(); } + + protected bool Equals(Journal other) => Start.Equals(other.Start) && Equals(other as RecurringComponent); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((Journal)obj); + } + + public override int GetHashCode() + { + var hashCode = Start?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ base.GetHashCode(); + return hashCode; + } } } \ No newline at end of file diff --git a/v2/ical.NET/Components/RecurringComponent.cs b/v2/ical.NET/Components/RecurringComponent.cs index 61e32f6bb..fba8ed115 100644 --- a/v2/ical.NET/Components/RecurringComponent.cs +++ b/v2/ical.NET/Components/RecurringComponent.cs @@ -226,5 +226,52 @@ public virtual IList PollAlarms(IDateTime startTime, IDateTime } return occurrences; } + + protected bool Equals(RecurringComponent other) + { + var result = Equals(DtStart, other.DtStart) + && Equals(Priority, other.Priority) + && string.Equals(Summary, other.Summary, StringComparison.OrdinalIgnoreCase) + && string.Equals(Class, other.Class, StringComparison.OrdinalIgnoreCase) + && string.Equals(Description, other.Description, StringComparison.OrdinalIgnoreCase) + && Equals(RecurrenceId, other.RecurrenceId) + && Attachments.SequenceEqual(other.Attachments) + && CollectionHelpers.Equals(Categories, other.Categories) + && CollectionHelpers.Equals(Contacts, other.Contacts) + && CollectionHelpers.Equals(ExceptionDates, other.ExceptionDates) + && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) + && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, orderSignificant: true) + && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, orderSignificant: true); + + return result; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((RecurringComponent)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = DtStart?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ Priority.GetHashCode(); + hashCode = (hashCode * 397) ^ (Summary?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Class?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Description?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (RecurrenceId?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Attachments); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Categories); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Contacts); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); + return hashCode; + } + } } } \ No newline at end of file