Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for Collection was modified exception. Calling GetOccurrences sho… #207

Merged
merged 1 commit into from
Dec 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ A listing of what each [Nuget package](https://www.nuget.org/packages/Ical.Net)

### v2

* 2.2.25: Fix for Collection was modified exception, and better handling of recurrence ids when calling `Calendar.GetOccurrences` ([#188](https://github.com/rianjs/ical.net/issues/188)) ([#148](https://github.com/rianjs/ical.net/issues/148))
* 2.2.24: Performance enhancement for solidus-prefixed time zones ([#204](https://github.com/rianjs/ical.net/issues/204)).
* 2.2.23: Bugfix for culture for geographic location serialization. RFC-5545 requires coordinates to use decimal points, not commas, regardless of culture. Correct: `1.2345`. Incorrect: `1,2345`. ([#202](https://github.com/rianjs/ical.net/issues/202))
* 2.2.22: Bugfix for `Event` serialization that always changed the `Duration` to 0. Serialization shouldn't have side effects. ([#199](https://github.com/rianjs/ical.net/issues/199))
Expand Down
2 changes: 1 addition & 1 deletion v2/Ical.Net.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Ical.Net</id>
<version>2.2.24</version>
<version>2.2.25</version>
<title>Ical.Net</title>
<authors>Rian Stockbower, Douglas Day, M. David Peterson</authors>
<owners>Rian Stockbower</owners>
Expand Down
122 changes: 122 additions & 0 deletions v2/ical.NET.UnitTests/GetOccurrenceTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Linq;
using Ical.Net.DataTypes;
using NUnit.Framework;
Expand All @@ -7,6 +8,9 @@ namespace Ical.Net.UnitTests
{
internal class GetOccurrenceTests
{
public static CalendarCollection GetCalendars(string incoming) =>
Calendar.LoadFromStream(new StringReader(incoming)) as CalendarCollection;

[Test]
public void WrongDurationTest()
{
Expand Down Expand Up @@ -46,5 +50,123 @@ public void WrongDurationTest()
Assert.AreEqual(secondStartCopy, secondOccurrence.Period.StartTime);
Assert.AreEqual(secondEndCopy, secondOccurrence.Period.EndTime);
}

[Test]
public void EnumerationChangedException()
{
const string ical = @"BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:name
X-WR-TIMEZONE:America/New_York
BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:DAYLIGHT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE

BEGIN:VEVENT
DTSTART;TZID=America/New_York:20161011T170000
DTEND;TZID=America/New_York:20161011T180000
DTSTAMP:20160930T115710Z
UID:blablabla
RECURRENCE-ID;TZID=America/New_York:20161011T170000
CREATED:20160830T144559Z
DESCRIPTION:
LAST-MODIFIED:20160928T142659Z
LOCATION:Location1
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Summary1
TRANSP:OPAQUE
END:VEVENT

END:VCALENDAR";

var calendar = GetCalendars(ical);
var date = new DateTime(2016, 10, 11);
var occurrences = calendar[0].GetOccurrences(date);

//We really want to make sure this doesn't explode
Assert.AreEqual(1, occurrences.Count);
}

[Test]
public void GetOccurrencesShouldEnumerate()
{
const string ical =
@"BEGIN:VCALENDAR
PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:W. Europe Standard Time
BEGIN:STANDARD
DTSTART:16010101T030000
RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTH=10;BYSETPOS=-1
TZNAME:Mitteleuropäische Zeit
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:00010101T020000
RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTH=3;BYSETPOS=-1
TZNAME:Mitteleuropäische Sommerzeit
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
BACKGROUND:BUSY
DESCRIPTION:Backup Daten
DTEND;TZID=W. Europe Standard Time:20150305T043000
DTSTAMP:20161122T120652Z
DTSTART;TZID=W. Europe Standard Time:20150305T000100
RESOURCES:server
RRULE:FREQ=WEEKLY;BYDAY=MO
SUMMARY:Server
UID:a30ed847-8000-4c53-9e58-99c8f9cf7c4b
X-LIGHTSOUT-ACTION:START=WakeUp\;END=Reboot\,Force
X-LIGHTSOUT-MODE:TimeSpan
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
END:VEVENT
BEGIN:VEVENT
BACKGROUND:BUSY
DESCRIPTION:Backup Daten
DTEND;TZID=W. Europe Standard Time:20161128T043000
DTSTAMP:20161122T120652Z
DTSTART;TZID=W. Europe Standard Time:20161128T000100
RECURRENCE-ID:20161128T000100
RESOURCES:server
SEQUENCE:0
SUMMARY:Server
UID:a30ed847-8000-4c53-9e58-99c8f9cf7c4b
X-LIGHTSOUT-ACTION:START=WakeUp\;END=Reboot\,Force
X-LIGHTSOUT-MODE:TimeSpan
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
END:VEVENT
END:VCALENDAR
";

var collection = Calendar.LoadFromStream(new StringReader(ical));
var startCheck = new DateTime(2016, 11, 11);
var occurrences = collection.GetOccurrences<Event>(startCheck, startCheck.AddMonths(1));

Assert.IsTrue(occurrences.Count == 4);
}
}
}
16 changes: 11 additions & 5 deletions v2/ical.NET/Calendar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,13 +401,19 @@ public virtual HashSet<Occurrence> GetOccurrences<T>(DateTime dt) where T : IRec
/// <param name="endTime">The ending date range</param>
public virtual HashSet<Occurrence> GetOccurrences<T>(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent
{
var occurrences = new HashSet<Occurrence>(RecurringItems.OfType<T>().SelectMany(recurrable => recurrable.GetOccurrences(startTime, endTime)));

occurrences.ExceptWith(
occurrences.Where(o => o.Source is IUniqueComponent)
var occurrences = new HashSet<Occurrence>(RecurringItems
.OfType<T>()
.SelectMany(recurrable => recurrable.GetOccurrences(startTime, endTime)));

var removeOccurrencesQuery = occurrences
.Where(o => o.Source is UniqueComponent)
.GroupBy(o => ((UniqueComponent)o.Source).Uid)
.SelectMany(group => group
.Where(o => o.Source.RecurrenceId != null)
.Where(o => o.Source.RecurrenceId.Equals(o.Period.StartTime)));
.SelectMany(occurrence => group.
Where(o => o.Source.RecurrenceId == null && occurrence.Source.RecurrenceId.Date.Equals(o.Period.StartTime.Date))));

occurrences.ExceptWith(removeOccurrencesQuery);
return occurrences;
}

Expand Down
1 change: 1 addition & 0 deletions v3/ical.NET.UnitTests/Ical.Net.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<Compile Include="FreeBusyTest.cs" />
<Compile Include="CalendarEventTest.cs" />
<Compile Include="CopyTest.cs" />
<Compile Include="GetOccurrenceTests.cs" />
<Compile Include="IcsFiles.cs" />
<Compile Include="JournalTest.cs" />
<Compile Include="ListExtensions.cs" />
Expand Down
16 changes: 11 additions & 5 deletions v3/ical.NET/Calendar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,13 +401,19 @@ public virtual HashSet<Occurrence> GetOccurrences<T>(DateTime dt) where T : IRec
/// <param name="endTime">The ending date range</param>
public virtual HashSet<Occurrence> GetOccurrences<T>(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent
{
var occurrences = new HashSet<Occurrence>(RecurringItems.OfType<T>().SelectMany(recurrable => recurrable.GetOccurrences(startTime, endTime)));

occurrences.ExceptWith(
occurrences.Where(o => o.Source is IUniqueComponent)
var occurrences = new HashSet<Occurrence>(RecurringItems
.OfType<T>()
.SelectMany(recurrable => recurrable.GetOccurrences(startTime, endTime)));

var removeOccurrencesQuery = occurrences
.Where(o => o.Source is UniqueComponent)
.GroupBy(o => ((UniqueComponent)o.Source).Uid)
.SelectMany(group => group
.Where(o => o.Source.RecurrenceId != null)
.Where(o => o.Source.RecurrenceId.Equals(o.Period.StartTime)));
.SelectMany(occurrence => group.
Where(o => o.Source.RecurrenceId == null && occurrence.Source.RecurrenceId.Date.Equals(o.Period.StartTime.Date))));

occurrences.ExceptWith(removeOccurrencesQuery);
return occurrences;
}

Expand Down
163 changes: 163 additions & 0 deletions v3/ical.net.unittests/GetOccurrenceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using System;
using System.IO;
using System.Linq;
using Ical.Net.DataTypes;
using NUnit.Framework;

namespace Ical.Net.UnitTests
{
internal class GetOccurrenceTests
{
public static CalendarCollection GetCalendars(string incoming) => Calendar.LoadFromStream(new StringReader(incoming));

[Test]
public void WrongDurationTest()
{
var firstStart = new CalDateTime(DateTime.Parse("2016-01-01"));
var firstEnd = new CalDateTime(DateTime.Parse("2016-01-05"));
var vEvent = new CalendarEvent {DtStart = firstStart, DtEnd = firstEnd,};

var secondStart = new CalDateTime(DateTime.Parse("2016-03-01"));
var secondEnd = new CalDateTime(DateTime.Parse("2016-03-05"));
var vEvent2 = new CalendarEvent {DtStart = secondStart, DtEnd = secondEnd,};

var calendar = new Calendar();
calendar.Events.Add(vEvent);
calendar.Events.Add(vEvent2);

var searchStart = DateTime.Parse("2015-12-29");
var searchEnd = DateTime.Parse("2017-02-10");
var occurrences = calendar.GetOccurrences(searchStart, searchEnd).OrderBy(o => o.Period.StartTime).ToList();

var firstOccurrence = occurrences.First();
var firstStartCopy = firstStart.Copy<CalDateTime>();
var firstEndCopy = firstEnd.Copy<CalDateTime>();
Assert.AreEqual(firstStartCopy, firstOccurrence.Period.StartTime);
Assert.AreEqual(firstEndCopy, firstOccurrence.Period.EndTime);

var secondOccurrence = occurrences.Last();
var secondStartCopy = secondStart.Copy<CalDateTime>();
var secondEndCopy = secondEnd.Copy<CalDateTime>();
Assert.AreEqual(secondStartCopy, secondOccurrence.Period.StartTime);
Assert.AreEqual(secondEndCopy, secondOccurrence.Period.EndTime);
}

[Test]
public void EnumerationChangedException()
{
const string ical = @"BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:name
X-WR-TIMEZONE:America/New_York
BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:DAYLIGHT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
END:VTIMEZONE

BEGIN:VEVENT
DTSTART;TZID=America/New_York:20161011T170000
DTEND;TZID=America/New_York:20161011T180000
DTSTAMP:20160930T115710Z
UID:blablabla
RECURRENCE-ID;TZID=America/New_York:20161011T170000
CREATED:20160830T144559Z
DESCRIPTION:
LAST-MODIFIED:20160928T142659Z
LOCATION:Location1
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Summary1
TRANSP:OPAQUE
END:VEVENT

END:VCALENDAR";

var calendar = GetCalendars(ical);
var date = new DateTime(2016, 10, 11);
var occurrences = calendar[0].GetOccurrences(date);

//We really want to make sure this doesn't explode
Assert.AreEqual(1, occurrences.Count);
}

[Test]
public void GetOccurrencesShouldEnumerate()
{
const string ical =
@"BEGIN:VCALENDAR
PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:W. Europe Standard Time
BEGIN:STANDARD
DTSTART:16010101T030000
RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTH=10;BYSETPOS=-1
TZNAME:Mitteleuropäische Zeit
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:00010101T020000
RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTH=3;BYSETPOS=-1
TZNAME:Mitteleuropäische Sommerzeit
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
BACKGROUND:BUSY
DESCRIPTION:Backup Daten
DTEND;TZID=W. Europe Standard Time:20150305T043000
DTSTAMP:20161122T120652Z
DTSTART;TZID=W. Europe Standard Time:20150305T000100
RESOURCES:server
RRULE:FREQ=WEEKLY;BYDAY=MO
SUMMARY:Server
UID:a30ed847-8000-4c53-9e58-99c8f9cf7c4b
X-LIGHTSOUT-ACTION:START=WakeUp\;END=Reboot\,Force
X-LIGHTSOUT-MODE:TimeSpan
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
END:VEVENT
BEGIN:VEVENT
BACKGROUND:BUSY
DESCRIPTION:Backup Daten
DTEND;TZID=W. Europe Standard Time:20161128T043000
DTSTAMP:20161122T120652Z
DTSTART;TZID=W. Europe Standard Time:20161128T000100
RECURRENCE-ID:20161128T000100
RESOURCES:server
SEQUENCE:0
SUMMARY:Server
UID:a30ed847-8000-4c53-9e58-99c8f9cf7c4b
X-LIGHTSOUT-ACTION:START=WakeUp\;END=Reboot\,Force
X-LIGHTSOUT-MODE:TimeSpan
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
END:VEVENT
END:VCALENDAR
";

var collection = Calendar.LoadFromStream(new StringReader(ical));
var startCheck = new DateTime(2016, 11, 11);
var occurrences = collection.GetOccurrences<CalendarEvent>(startCheck, startCheck.AddMonths(1));

Assert.IsTrue(occurrences.Count == 4);
}
}
}