Skip to content

Commit a0179f8

Browse files
authored
Implement deep copy methods for components (#620)
* Migrated Ical.Net.Tests rpoject from NUnit3 to NUnit 4 * Migrate NUnit 3.14.0 to 4.2.2 * Add NUnit.Analyzers 4.3.0 * Convert Classic Assert to Constraint Model * Introduce Assert.Multiple to group assertions, ensuring all are evaluated even if some fail. * Simplify test case source return types to IEnumerable without a type argument * Remove unused using directives and added necessary ones. Test logic is left unchanged except for 7 tests in Exception context. Here all try...catch blocks are replaced with `Throws.` assertions. * Implement deep copy methods for components - Implemented `CopyFrom` method in multiple classes for deep copying. - Removed questions in code and implemented solution - Updated `ICopyable` interface documentation. - Introduced pattern matching for better readability - Added new test methods in `CopyComponentTests` class for deep copying various calendar components. - Fixed broken SerializationTests.AttendeesSerialized() (Corrected attendee names and fixed typos in assertions.) - Refactored `CalendarObjectBase` (should eventually become abstract) - Added `ExcludeFromCodeCoverage` attribute to `CalendarObjectList`. - Updated `Ical.Net.csproj` to latest C# version. Fixes #149 * Rename parameter 'c' to 'obj' to match the interface declaration. * Implement changes from review * Removed redundant check in `Attachment.cs` `CopyFrom` method. * Updated `Period.cs` and `Trigger.cs` to use `Copy<IDateTime>()` method. * Modified `Alarm` class to implement `IComparable<Alarm>` with `CompareTo` method.
1 parent 564aaa0 commit a0179f8

34 files changed

+403
-231
lines changed
Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
3-
<PropertyGroup>
4-
<TargetFrameworks>net8.0;net6.0;netcoreapp3.1;net48</TargetFrameworks>
5-
<OutputType>Exe</OutputType>
6-
<LangVersion>latest</LangVersion>
7-
</PropertyGroup>
8-
9-
<ItemGroup>
10-
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
11-
</ItemGroup>
12-
13-
<ItemGroup>
14-
<ProjectReference Include="..\Ical.Net\Ical.Net.csproj" />
15-
</ItemGroup>
16-
2+
<PropertyGroup>
3+
<TargetFrameworks>net8.0;net6.0;netcoreapp3.1;net48</TargetFrameworks>
4+
<OutputType>Exe</OutputType>
5+
<LangVersion>latest</LangVersion>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
9+
</ItemGroup>
10+
<ItemGroup>
11+
<ProjectReference Include="..\Ical.Net\Ical.Net.csproj" />
12+
</ItemGroup>
1713
</Project>

Ical.Net.Tests/CopyComponentTests.cs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
using Ical.Net.CalendarComponents;
2+
using Ical.Net.DataTypes;
3+
using Ical.Net.Serialization;
4+
using NUnit.Framework;
5+
using System;
6+
using System.Collections;
7+
using System.Collections.Generic;
8+
using System.Text.RegularExpressions;
9+
10+
namespace Ical.Net.Tests
11+
{
12+
/// <summary>
13+
/// Tests for deep copying of ICal components.
14+
/// </summary>
15+
[TestFixture]
16+
public class CopyComponentTests
17+
{
18+
[Test, TestCaseSource(nameof(CopyCalendarTest_TestCases)), Category("Copy tests")]
19+
public void CopyCalendarTest(string calendarString)
20+
{
21+
var iCal1 = Calendar.Load(calendarString);
22+
var iCal2 = iCal1.Copy<Calendar>();
23+
SerializationTests.CompareCalendars(iCal1, iCal2);
24+
}
25+
26+
public static IEnumerable CopyCalendarTest_TestCases()
27+
{
28+
yield return new TestCaseData(IcsFiles.Attachment3).SetName("Attachment3");
29+
yield return new TestCaseData(IcsFiles.Bug2148092).SetName("Bug2148092");
30+
yield return new TestCaseData(IcsFiles.CaseInsensitive1).SetName("CaseInsensitive1");
31+
yield return new TestCaseData(IcsFiles.CaseInsensitive2).SetName("CaseInsensitive2");
32+
yield return new TestCaseData(IcsFiles.CaseInsensitive3).SetName("CaseInsensitive3");
33+
yield return new TestCaseData(IcsFiles.Categories1).SetName("Categories1");
34+
yield return new TestCaseData(IcsFiles.Duration1).SetName("Duration1");
35+
yield return new TestCaseData(IcsFiles.Encoding1).SetName("Encoding1");
36+
yield return new TestCaseData(IcsFiles.Event1).SetName("Event1");
37+
yield return new TestCaseData(IcsFiles.Event2).SetName("Event2");
38+
yield return new TestCaseData(IcsFiles.Event3).SetName("Event3");
39+
yield return new TestCaseData(IcsFiles.Event4).SetName("Event4");
40+
yield return new TestCaseData(IcsFiles.GeographicLocation1).SetName("GeographicLocation1");
41+
yield return new TestCaseData(IcsFiles.Language1).SetName("Language1");
42+
yield return new TestCaseData(IcsFiles.Language2).SetName("Language2");
43+
yield return new TestCaseData(IcsFiles.Language3).SetName("Language3");
44+
yield return new TestCaseData(IcsFiles.TimeZone1).SetName("TimeZone1");
45+
yield return new TestCaseData(IcsFiles.TimeZone2).SetName("TimeZone2");
46+
yield return new TestCaseData(IcsFiles.TimeZone3).SetName("TimeZone3");
47+
yield return new TestCaseData(IcsFiles.XProperty1).SetName("XProperty1");
48+
yield return new TestCaseData(IcsFiles.XProperty2).SetName("XProperty2");
49+
}
50+
51+
private static readonly DateTime _now = DateTime.Now;
52+
private static readonly DateTime _later = _now.AddHours(1);
53+
54+
private static CalendarEvent GetSimpleEvent() => new CalendarEvent
55+
{
56+
DtStart = new CalDateTime(_now),
57+
DtEnd = new CalDateTime(_later),
58+
Duration = TimeSpan.FromHours(1),
59+
};
60+
61+
private static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } });
62+
63+
[Test]
64+
public void CopyCalendarEventTest()
65+
{
66+
var orig = GetSimpleEvent();
67+
orig.Uid = "Hello";
68+
orig.Summary = "Original summary";
69+
orig.Resources = new[] { "A", "B" };
70+
orig.GeographicLocation = new GeographicLocation(48.210033, 16.363449);
71+
orig.Transparency = TransparencyType.Opaque;
72+
orig.Attachments.Add(new Attachment("https://original.org/"));
73+
var copy = orig.Copy<CalendarEvent>();
74+
75+
copy.Uid = "Goodbye";
76+
copy.Summary = "Copy summary";
77+
78+
var resourcesCopyFromOrig = new List<string>(copy.Resources);
79+
copy.Resources = new[] { "C", "D" };
80+
copy.Attachments[0].Uri = new Uri("https://copy.org/");
81+
const string uidPattern = "UID:";
82+
var serializedOrig = SerializeEvent(orig);
83+
var serializedCopy = SerializeEvent(copy);
84+
85+
Assert.Multiple(() =>
86+
{
87+
// Should be a deep copy and changes only apply to the copy instance
88+
Assert.That(copy.Uid, Is.Not.EqualTo(orig.Uid));
89+
Assert.That(copy.Summary, Is.Not.EqualTo(orig.Summary));
90+
Assert.That(copy.Attachments[0].Uri, Is.Not.EqualTo(orig.Attachments[0].Uri));
91+
Assert.That(copy.Resources[0], Is.Not.EqualTo(orig.Resources[0]));
92+
93+
Assert.That(resourcesCopyFromOrig, Is.EquivalentTo(orig.Resources));
94+
Assert.That(copy.GeographicLocation, Is.EqualTo(orig.GeographicLocation));
95+
Assert.That(copy.Transparency, Is.EqualTo(orig.Transparency));
96+
97+
Assert.That(Regex.Matches(serializedOrig, uidPattern, RegexOptions.Compiled, TimeSpan.FromSeconds(100)), Has.Count.EqualTo(1));
98+
Assert.That(Regex.Matches(serializedCopy, uidPattern, RegexOptions.Compiled, TimeSpan.FromSeconds(100)), Has.Count.EqualTo(1));
99+
});
100+
}
101+
102+
[Test]
103+
public void CopyFreeBusyTest()
104+
{
105+
var orig = new FreeBusy
106+
{
107+
Start = new CalDateTime(_now),
108+
End = new CalDateTime(_later),
109+
Entries = { new FreeBusyEntry { Language = "English", StartTime = new CalDateTime(2024, 10, 1), Duration = TimeSpan.FromDays(1), Status = FreeBusyStatus.Busy}}
110+
};
111+
112+
var copy = orig.Copy<FreeBusy>();
113+
114+
Assert.Multiple(() =>
115+
{
116+
// Start/DtStart and End/DtEnd are the same
117+
Assert.That(copy.Start, Is.EqualTo(orig.DtStart));
118+
Assert.That(copy.End, Is.EqualTo(orig.DtEnd));
119+
Assert.That(copy.Entries[0].Language, Is.EqualTo(orig.Entries[0].Language));
120+
Assert.That(copy.Entries[0].StartTime, Is.EqualTo(orig.Entries[0].StartTime));
121+
Assert.That(copy.Entries[0].Duration, Is.EqualTo(orig.Entries[0].Duration));
122+
Assert.That(copy.Entries[0].Status, Is.EqualTo(orig.Entries[0].Status));
123+
});
124+
}
125+
126+
[Test]
127+
public void CopyAlarmTest()
128+
{
129+
var orig = new Alarm
130+
{
131+
Action = AlarmAction.Display,
132+
Trigger = new Trigger(TimeSpan.FromMinutes(15)),
133+
Description = "Test Alarm"
134+
};
135+
136+
var copy = orig.Copy<Alarm>();
137+
138+
Assert.Multiple(() =>
139+
{
140+
Assert.That(copy.Action, Is.EqualTo(orig.Action));
141+
Assert.That(copy.Trigger, Is.EqualTo(orig.Trigger));
142+
Assert.That(copy.Description, Is.EqualTo(orig.Description));
143+
});
144+
}
145+
146+
[Test]
147+
public void CopyTodoTest()
148+
{
149+
var orig = new Todo
150+
{
151+
Summary = "Test Todo",
152+
Description = "This is a test todo",
153+
Due = new CalDateTime(DateTime.Now.AddDays(10)),
154+
Priority = 1,
155+
Contacts = new[] { "John", "Paul" },
156+
Status = "NeedsAction"
157+
};
158+
159+
var copy = orig.Copy<Todo>();
160+
161+
Assert.Multiple(() =>
162+
{
163+
Assert.That(copy.Summary, Is.EqualTo(orig.Summary));
164+
Assert.That(copy.Description, Is.EqualTo(orig.Description));
165+
Assert.That(copy.Due, Is.EqualTo(orig.Due));
166+
Assert.That(copy.Priority, Is.EqualTo(orig.Priority));
167+
Assert.That(copy.Contacts, Is.EquivalentTo(orig.Contacts));
168+
Assert.That(copy.Status, Is.EqualTo(orig.Status));
169+
});
170+
}
171+
172+
[Test]
173+
public void CopyJournalTest()
174+
{
175+
var orig = new Journal
176+
{
177+
Summary = "Test Journal",
178+
Description = "This is a test journal",
179+
DtStart = new CalDateTime(DateTime.Now),
180+
Categories = new List<string> { "Category1", "Category2" },
181+
Priority = 1,
182+
Status = "Draft"
183+
};
184+
185+
var copy = orig.Copy<Journal>();
186+
187+
Assert.Multiple(() =>
188+
{
189+
Assert.That(copy.Summary, Is.EqualTo(orig.Summary));
190+
Assert.That(copy.Description, Is.EqualTo(orig.Description));
191+
Assert.That(copy.DtStart, Is.EqualTo(orig.DtStart));
192+
Assert.That(copy.Categories, Is.EquivalentTo(orig.Categories));
193+
Assert.That(copy.Priority, Is.EqualTo(orig.Priority));
194+
Assert.That(copy.Status, Is.EqualTo(orig.Status));
195+
});
196+
}
197+
}
198+
}

Ical.Net.Tests/CopyTest.cs

Lines changed: 0 additions & 77 deletions
This file was deleted.

Ical.Net.Tests/DeserializationTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ public void Encoding2()
229229
var evt = iCal.Events.First();
230230

231231
Assert.That(
232-
evt.Attachments[0].ToString(),
233-
Is.EqualTo("This is a test to try out base64 encoding without being too large.\r\n" +
232+
evt.Attachments[0].ToString(),
233+
Is.EqualTo("This is a test to try out base64 encoding without being too large.\r\n" +
234234
"This is a test to try out base64 encoding without being too large.\r\n" +
235235
"This is a test to try out base64 encoding without being too large.\r\n" +
236236
"This is a test to try out base64 encoding without being too large.\r\n" +
@@ -242,7 +242,7 @@ public void Encoding2()
242242
"This is a test to try out base64 encoding without being too large.\r\n" +
243243
"This is a test to try out base64 encoding without being too large.\r\n" +
244244
"This is a test to try out base64 encoding without being too large."),
245-
"Attached value does not match.");
245+
"Attached value does not match.");
246246
}
247247

248248
[Test]

Ical.Net.Tests/Ical.Net.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<TargetFrameworks>net8.0;net6.0;net48</TargetFrameworks>
44
<SignAssembly>true</SignAssembly>
55
<AssemblyOriginatorKeyFile>..\IcalNetStrongnameKey.snk</AssemblyOriginatorKeyFile>
6+
<LangVersion>latest</LangVersion>
67
</PropertyGroup>
78
<ItemGroup>
89
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />

Ical.Net.Tests/RecurrenceTests.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ int eventIndex
3737
.OrderBy(o => o.Period.StartTime)
3838
.ToList();
3939

40-
Assert.That(occurrences,
41-
Has.Count.EqualTo(dateTimes.Length),
42-
"There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count);
40+
Assert.That(
41+
occurrences,
42+
Has.Count.EqualTo(dateTimes.Length),
43+
"There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count);
4344

4445
if (evt.RecurrenceRules.Count > 0)
4546
{

0 commit comments

Comments
 (0)