- 
                Notifications
    
You must be signed in to change notification settings  - Fork 247
 
          Fix: Event with RECURRENCE-ID overrides occurrence from orginal event
          #864
        
          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
Conversation
          Codecov Report❌ Patch coverage is  
 ❌ Your patch status has failed because the patch coverage (65.9%) is below the target coverage (80.0%). You can increase the patch coverage or adjust the target coverage. @@           Coverage Diff           @@
##            main    #864     +/-   ##
=======================================
- Coverage   68.0%   67.9%   -0.1%     
=======================================
  Files        112     112             
  Lines       4267    4297     +30     
  Branches     956     973     +17     
=======================================
+ Hits        2901    2917     +16     
+ Misses      1038    1037      -1     
- Partials     328     343     +15     
 ... and 1 file with indirect coverage changes 🚀 New features to boost your workflow:
  | 
    
| 
           @axunonb I applied this fix to my earlier branch and minimized the ics data but that still seems to not generate all occurrences: main...ramonsmits:ical.net:occurence-issue I also pushed the same test but for v4 that does generate the expected occurrences:  | 
    
| 
           Yes, I didn't use the same DTSTART in Master and Override event. That seems to be the reason, and I got excited too soon ;-)  | 
    
| 
           @ramonsmits Should work now also when the Override Event has the same DTSTART  | 
    
Filter UID/RECURRENCE-ID combinations that replace other occurrences before using `.OrderedDistinct()`
830295e    to
    0c92c39      
    Compare
  
    | .OrderedMergeMany() | ||
| 
               | 
          ||
| // Remove duplicates and take advantage of being ordered to avoid full enumeration. | ||
| .OrderedDistinct() | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the expected order? Looking at the implementation of OrderedDistinct it it keeps the order of the enumerator and the first of its "sequence" is kept. However, in the code above I do not see any sorting happening. Is there some order implied from the RecurringItems items?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It ordered by Period.StartTime
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the question is legit. The sequence is ordered by StartTime, true, but OrderedDistinct() uses Ocurrences.Equals() to compare for equality, which compares more than just the StartTime, i.e. also the Source. But the sequence is not ordered by Source, so this could become a problem.
Actually the real core of the problem we are dealing with here is a shortcoming (or misuse) of Occurrences.Equals, which compares the Source property via UniqueComponent.Equals under the hood, which in turn only looks at the objects' Uid. In case of VEVENTs with RECURRENCE-ID there are multiple objects having the same UID. In the case of this issue they also have the same DTSTART, so the resulting Occurrence instances are considered equal although they aren't. OrderedDistinct() will therefore, more or less randomly, return only one of both (except if there are even more entries with the same StartDate and the other issue kicks in that the sequence is not properly ordered due to the Source property). As a consequence the filter for the RECURRENCE-ID that has formerly been placed after the OrderedDistinct() couldn't see it.
While this change is a significant improvement, it is not a final fix for that problem. Actually it should be clearly defined how Occurrence instances are compared for equality and I'd say this should not happen based on just the UID. Actually I'd say that two Occurrences should be considered equal only if the Sources are equal by reference.
| 
           btw, tested last against my own ical and it seems to now correctly generate the occurrences 💪  | 
    
* Move tests from GetOccurrencesTests to RecurrenceTests * Delete GetOccurrencesTests * Minor refactoring of some unit tests
0c92c39    to
    c79005e      
    Compare
  
    There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| } | ||
| 
               | 
          ||
| [Test] | ||
| public void GetOccurrencesWithRecurrenceId_DateOnly_ShouldEnumerate() | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new tests actually don't reproduce the original issue. Maybe add the test from the ticket?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will flag the tests with Category `RECURRENCE-ID" and add a comment referencing to the test covering the #863
        
          
                Ical.Net/Calendar.cs
              
                Outdated
          
        
      | .Where(r => r.Uid != null) | ||
| // Create a value tuple instead of an anonymous type, because | ||
| // anonymous types don't work well as dictionary keys due to equality semantics. | ||
| .Select(r => ((r as IUniqueComponent)?.Uid, r.RecurrenceId!.Value)) | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fully agree that it is better to use tuples here rather than anonymous types but I don't think this is the root cause of the problem (as suspected in the issue's comments).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fully agree
        
          
                Ical.Net/Calendar.cs
              
                Outdated
          
        
      | // Exclude occurrences that are overridden by other components with the same UID and RECURRENCE-ID. | ||
| // This must happen before .OrderedDistinct() because that method would remove duplicates | ||
| // based on the occurrence time, and we need to remove them based on UID + RECURRENCE-ID. | ||
| .Where(r => | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably just a matter of taste: For simplicity I'd rather place the filter after the ToList(). The idea is that before the ToList() we only deal with the individual sequences of occurrences, but not with the occurrences themselves, which is done only after the ToList(). If the filter is placed here, the effect is the same, but the code is less linear to read. I suggest to place it after the OrderedMergeMany() and before the OrderedDistinct().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, due to #455 it might be a good idea to keep this filter here, or at least before the OrderedMerge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will leave the position unchanged
| .OrderedMergeMany() | ||
| 
               | 
          ||
| // Remove duplicates and take advantage of being ordered to avoid full enumeration. | ||
| .OrderedDistinct() | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the question is legit. The sequence is ordered by StartTime, true, but OrderedDistinct() uses Ocurrences.Equals() to compare for equality, which compares more than just the StartTime, i.e. also the Source. But the sequence is not ordered by Source, so this could become a problem.
Actually the real core of the problem we are dealing with here is a shortcoming (or misuse) of Occurrences.Equals, which compares the Source property via UniqueComponent.Equals under the hood, which in turn only looks at the objects' Uid. In case of VEVENTs with RECURRENCE-ID there are multiple objects having the same UID. In the case of this issue they also have the same DTSTART, so the resulting Occurrence instances are considered equal although they aren't. OrderedDistinct() will therefore, more or less randomly, return only one of both (except if there are even more entries with the same StartDate and the other issue kicks in that the sequence is not properly ordered due to the Source property). As a consequence the filter for the RECURRENCE-ID that has formerly been placed after the OrderedDistinct() couldn't see it.
While this change is a significant improvement, it is not a final fix for that problem. Actually it should be clearly defined how Occurrence instances are compared for equality and I'd say this should not happen based on just the UID. Actually I'd say that two Occurrences should be considered equal only if the Sources are equal by reference.
8adedbb    to
    bae25e0      
    Compare
  
    * Allow more than one `VEVENT` with same `UID` and `RECURRENCE-ID` (threw before) * Use the latest `UID` / `RECURRENCE-ID`: * either last override in the calendar * or override with the highest `SEQUENCE`
bae25e0    to
    3cb8fdd      
    Compare
  
    
          
 | 
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice!
| 
           @minichma Thanks for your support and review  | 
    
| 
           @axunonb urw  | 
    



Resolves #863
Resolves #865