kopia lustrzana https://github.com/collective/icalendar
Merge branch 'master' into test_restructure_issue_168
commit
76b51ae8b6
|
@ -0,0 +1,14 @@
|
|||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
BEGIN:VEVENT
|
||||
DTSTART:20140401T000000Z
|
||||
DTEND:20140401T010000Z
|
||||
DTSTAMP:20140401T000000Z
|
||||
SUMMARY:Broken Eevnt
|
||||
CLASS:PUBLIC
|
||||
STATUS:CONFIRMED
|
||||
TRANSP:OPAQUE
|
||||
END:VEVENT
|
||||
X
|
||||
END:VCALENDAR
|
|
@ -1,10 +1,15 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
import icalendar
|
||||
import pytz
|
||||
from datetime import datetime
|
||||
from dateutil import tz
|
||||
try:
|
||||
import zoneinfo
|
||||
except ModuleNotFoundError:
|
||||
from backports import zoneinfo
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class DataSource:
|
||||
'''A collection of parsed ICS elements (e.g calendars, timezones, events)'''
|
||||
|
@ -13,18 +18,18 @@ class DataSource:
|
|||
self._data_source_folder = data_source_folder
|
||||
|
||||
def __getattr__(self, attribute):
|
||||
if not attribute in self.__dict__:
|
||||
source_file = attribute.replace('-', '_') + '.ics'
|
||||
source_path = os.path.join(self.__dict__['_data_source_folder'], source_file)
|
||||
with open(source_path, 'rb') as f:
|
||||
try:
|
||||
raw_ics = f.read()
|
||||
source = self.__dict__['_parser'](raw_ics)
|
||||
source.raw_ics = raw_ics
|
||||
self.__dict__[attribute] = source
|
||||
except ValueError as error:
|
||||
LOGGER.error(f'Could not load {source_file} due to {error}')
|
||||
return self.__dict__[attribute]
|
||||
"""Parse a file and return the result stored in the attribute."""
|
||||
source_file = attribute.replace('-', '_') + '.ics'
|
||||
source_path = os.path.join(self._data_source_folder, source_file)
|
||||
with open(source_path, 'rb') as f:
|
||||
raw_ics = f.read()
|
||||
source = self._parser(raw_ics)
|
||||
source.raw_ics = raw_ics
|
||||
self.__dict__[attribute] = source
|
||||
return source
|
||||
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.__dict__)
|
||||
|
@ -42,7 +47,15 @@ def timezones():
|
|||
def events():
|
||||
return DataSource(EVENTS_FOLDER, icalendar.Event.from_ical)
|
||||
|
||||
@pytest.fixture(params=[
|
||||
pytz.utc,
|
||||
zoneinfo.ZoneInfo('UTC'),
|
||||
pytz.timezone('UTC'),
|
||||
tz.UTC,
|
||||
tz.gettz('UTC')])
|
||||
def utc(request):
|
||||
return request.param
|
||||
|
||||
@pytest.fixture
|
||||
def calendars():
|
||||
return DataSource(CALENDARS_FOLDER, icalendar.Calendar.from_ical)
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
BEGIN:VEVENT
|
||||
SUMMARY;LANGUAGE=ru:te
|
||||
END:VEVENT
|
|
@ -0,0 +1,14 @@
|
|||
BEGIN:VEVENT
|
||||
SUMMARY:wichtiger termin 1
|
||||
DTSTART:20130416T100000Z
|
||||
DTEND:20130416T110000Z
|
||||
DTSTAMP:20130416T092616Z
|
||||
UID:20130416112341.10064jz0k4j7uem8@acmenet.de
|
||||
CLASS:PUBLIC
|
||||
CREATED:20130416T092341Z
|
||||
LAST-MODIFIED:20130416T092341Z
|
||||
LOCATION:im büro
|
||||
ORGANIZER;CN="acme, ädmin":mailto:adm-acme@mydomain.de
|
||||
STATUS:CONFIRMED
|
||||
TRANSP:OPAQUE
|
||||
END:VEVENT
|
|
@ -0,0 +1,10 @@
|
|||
BEGIN:VEVENT
|
||||
DTSTART:20140401T000000Z
|
||||
DTEND:20140401T010000Z
|
||||
DTSTAMP:20140401T000000Z
|
||||
SUMMARY:Broken Eevnt
|
||||
CLASS:PUBLIC
|
||||
STATUS:CONFIRMED
|
||||
TRANSP:OPAQUE
|
||||
X
|
||||
END:VEVENT
|
|
@ -1,30 +1,3 @@
|
|||
BEGIN:VCALENDAR
|
||||
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
||||
VERSION:2.0
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
X-WR-CALNAME:Market East
|
||||
X-WR-TIMEZONE:America/New_York
|
||||
X-WR-CALDESC:
|
||||
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:20130907T120000
|
||||
DTEND;TZID=America/New_York:20130907T170000
|
||||
|
@ -45,4 +18,3 @@ SUMMARY:Market East Live!
|
|||
TRANSP:OPAQUE
|
||||
END:VEVENT
|
||||
|
||||
END:VCALENDAR
|
|
@ -0,0 +1,4 @@
|
|||
BEGIN:VEVENT
|
||||
DTSTART:20150325T101010
|
||||
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU;
|
||||
END:VEVENT
|
|
@ -0,0 +1,6 @@
|
|||
BEGIN:VEVENT
|
||||
DTSTART:20150219T133000
|
||||
DTSTAMP:20150219T133000
|
||||
UID:1234567
|
||||
RDATE;VALUE=PERIOD:20150219T133000/PT10H
|
||||
END:VEVENT
|
|
@ -0,0 +1,19 @@
|
|||
BEGIN:VEVENT
|
||||
DTSTAMP:20120605T003759Z
|
||||
DTSTART;TZID=America/New_York:20120712T183000
|
||||
DTEND;TZID=America/New_York:20120712T213000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:DevOps DC Meetup
|
||||
DESCRIPTION:DevOpsDC\nThursday\, July 12 at 6:30 PM\n\nThis will be a joi
|
||||
nt meetup / hack night with the DC jQuery Users Group. The idea behind
|
||||
the hack night: Small teams consisting of at least 1 member...\n\nDeta
|
||||
ils: http://www.meetup.com/DevOpsDC/events/47635522/
|
||||
CLASS:PUBLIC
|
||||
CREATED:20120111T120339Z
|
||||
GEO:38.90;-77.01
|
||||
LOCATION:Fathom Creative\, Inc. (1333 14th Street Northwest\, Washington
|
||||
D.C.\, DC 20005)
|
||||
URL:http://www.meetup.com/DevOpsDC/events/47635522/
|
||||
LAST-MODIFIED:20120522T174406Z
|
||||
UID:event_qtkfrcyqkbnb@meetup.com
|
||||
END:VEVENT
|
|
@ -0,0 +1,3 @@
|
|||
BEGIN:VEVENT
|
||||
SUMMARY:abcdef
|
||||
END:VEVENT
|
|
@ -0,0 +1,3 @@
|
|||
BEGIN:VEVENT
|
||||
SUMMARY:åäö
|
||||
END:VEVENT
|
|
@ -0,0 +1,12 @@
|
|||
BEGIN:VEVENT
|
||||
CREATED:20081114T072804Z
|
||||
UID:D449CA84-00A3-4E55-83E1-34B58268853B
|
||||
DTEND:20070220T180000
|
||||
RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20070619T225959
|
||||
TRANSP:OPAQUE
|
||||
SUMMARY:Esb mellon phone conf
|
||||
DTSTART:20070220T170000
|
||||
DTSTAMP:20070221T095412Z
|
||||
SEQUENCE:0
|
||||
END:VEVENT
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import unittest
|
||||
|
||||
import pytest
|
||||
|
||||
import datetime
|
||||
import icalendar
|
||||
import os
|
||||
|
@ -88,3 +90,22 @@ class TestEncoding(unittest.TestCase):
|
|||
+ b'\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f\xc3\x84\xc3\x96\xc3\x9c\r\n'
|
||||
+ b'END:VEVENT\r\nEND:VCALENDAR\r\n'
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize('event_name', [
|
||||
# Non-unicode characters in summary
|
||||
'issue_64_event_with_non_unicode_summary',
|
||||
# Unicode characters in summary
|
||||
'issue_64_event_with_unicode_summary',
|
||||
# chokes on umlauts in ORGANIZER
|
||||
'issue_101_icalendar_chokes_on_umlauts_in_organizer'
|
||||
])
|
||||
def test_events_unicoded(events, event_name):
|
||||
'''Issue #64 - Event.to_ical() fails for unicode strings
|
||||
Issue #101 - icalendar is choking on umlauts in ORGANIZER
|
||||
|
||||
https://github.com/collective/icalendar/issues/64
|
||||
https://github.com/collective/icalendar/issues/101
|
||||
'''
|
||||
event = getattr(events, event_name)
|
||||
assert event.to_ical() == event.raw_ics
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from icalendar.parser_tools import to_unicode
|
||||
import unittest
|
||||
|
||||
import datetime
|
||||
|
@ -15,120 +14,6 @@ except ModuleNotFoundError:
|
|||
|
||||
class TestIssues(unittest.TestCase):
|
||||
|
||||
def test_issue_53(self):
|
||||
"""Issue #53 - Parsing failure on some descriptions?
|
||||
https://github.com/collective/icalendar/issues/53
|
||||
"""
|
||||
|
||||
directory = os.path.dirname(__file__)
|
||||
ics = open(os.path.join(directory, 'issue_53_parsing_failure.ics'),
|
||||
'rb')
|
||||
cal = icalendar.Calendar.from_ical(ics.read())
|
||||
ics.close()
|
||||
|
||||
event = cal.walk('VEVENT')[0]
|
||||
desc = event.get('DESCRIPTION')
|
||||
self.assertTrue(b'July 12 at 6:30 PM' in desc.to_ical())
|
||||
|
||||
timezones = cal.walk('VTIMEZONE')
|
||||
self.assertEqual(len(timezones), 1)
|
||||
tz = timezones[0]
|
||||
self.assertEqual(tz['tzid'].to_ical(), b"America/New_York")
|
||||
|
||||
def test_issue_55(self):
|
||||
"""Issue #55 - Parse error on utc-offset with seconds value
|
||||
https://github.com/collective/icalendar/issues/55
|
||||
"""
|
||||
ical_str = """BEGIN:VTIMEZONE
|
||||
TZID:America/Los Angeles
|
||||
BEGIN:STANDARD
|
||||
DTSTART:18831118T120702
|
||||
RDATE:18831118T120702
|
||||
TZNAME:PST
|
||||
TZOFFSETFROM:-075258
|
||||
TZOFFSETTO:-0800
|
||||
END:STANDARD
|
||||
END:VTIMEZONE"""
|
||||
|
||||
tz = icalendar.Timezone.from_ical(ical_str)
|
||||
self.assertEqual(
|
||||
tz.to_ical(),
|
||||
b'BEGIN:VTIMEZONE\r\nTZID:America/Los Angeles\r\n'
|
||||
b'BEGIN:STANDARD\r\n'
|
||||
b'DTSTART:18831118T120702\r\nRDATE:18831118T120702\r\nTZNAME:PST'
|
||||
b'\r\nTZOFFSETFROM:-075258\r\nTZOFFSETTO:-0800\r\n'
|
||||
b'END:STANDARD\r\n'
|
||||
b'END:VTIMEZONE\r\n')
|
||||
|
||||
def test_issue_58(self):
|
||||
"""Issue #58 - TZID on UTC DATE-TIMEs
|
||||
https://github.com/collective/icalendar/issues/58
|
||||
"""
|
||||
|
||||
# According to RFC 2445: "The TZID property parameter MUST NOT be
|
||||
# applied to DATE-TIME or TIME properties whose time values are
|
||||
# specified in UTC."
|
||||
|
||||
event = icalendar.Event()
|
||||
dt = pytz.utc.localize(datetime.datetime(2012, 7, 16, 0, 0, 0))
|
||||
event.add('dtstart', dt)
|
||||
self.assertEqual(
|
||||
event.to_ical(),
|
||||
b"BEGIN:VEVENT\r\n"
|
||||
b"DTSTART;VALUE=DATE-TIME:20120716T000000Z\r\n"
|
||||
b"END:VEVENT\r\n"
|
||||
)
|
||||
|
||||
def test_issue_64(self):
|
||||
"""Issue #64 - Event.to_ical() fails for unicode strings
|
||||
https://github.com/collective/icalendar/issues/64
|
||||
"""
|
||||
|
||||
# Non-unicode characters
|
||||
event = icalendar.Event()
|
||||
event.add("dtstart", datetime.datetime(2012, 9, 3, 0, 0, 0))
|
||||
event.add("summary", "abcdef")
|
||||
self.assertEqual(
|
||||
event.to_ical(),
|
||||
b"BEGIN:VEVENT\r\nSUMMARY:abcdef\r\nDTSTART;VALUE=DATE-TIME:"
|
||||
b"20120903T000000\r\nEND:VEVENT\r\n"
|
||||
)
|
||||
|
||||
# Unicode characters
|
||||
event = icalendar.Event()
|
||||
event.add("dtstart", datetime.datetime(2012, 9, 3, 0, 0, 0))
|
||||
event.add("summary", "åäö")
|
||||
self.assertEqual(
|
||||
event.to_ical(),
|
||||
b"BEGIN:VEVENT\r\nSUMMARY:\xc3\xa5\xc3\xa4\xc3\xb6\r\n"
|
||||
b"DTSTART;VALUE=DATE-TIME:20120903T000000\r\nEND:VEVENT\r\n"
|
||||
)
|
||||
|
||||
def test_issue_70(self):
|
||||
"""Issue #70 - e.decode("RRULE") causes Attribute Error
|
||||
https://github.com/collective/icalendar/issues/70
|
||||
"""
|
||||
|
||||
ical_str = """BEGIN:VEVENT
|
||||
CREATED:20081114T072804Z
|
||||
UID:D449CA84-00A3-4E55-83E1-34B58268853B
|
||||
DTEND:20070220T180000
|
||||
RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20070619T225959
|
||||
TRANSP:OPAQUE
|
||||
SUMMARY:Esb mellon phone conf
|
||||
DTSTART:20070220T170000
|
||||
DTSTAMP:20070221T095412Z
|
||||
SEQUENCE:0
|
||||
END:VEVENT"""
|
||||
|
||||
cal = icalendar.Calendar.from_ical(ical_str)
|
||||
recur = cal.decoded("RRULE")
|
||||
self.assertIsInstance(recur, icalendar.vRecur)
|
||||
self.assertEqual(
|
||||
recur.to_ical(),
|
||||
b'FREQ=WEEKLY;UNTIL=20070619T225959;INTERVAL=1'
|
||||
)
|
||||
|
||||
def test_issue_82(self):
|
||||
"""Issue #82 - vBinary __repr__ called rather than to_ical from
|
||||
container types
|
||||
|
@ -146,119 +31,6 @@ END:VEVENT"""
|
|||
b"VALUE=BINARY:dGV4dA==\r\nEND:VEVENT\r\n"
|
||||
)
|
||||
|
||||
def test_issue_100(self):
|
||||
"""Issue #100 - Transformed doctests into unittests, Test fixes and
|
||||
cleanup.
|
||||
https://github.com/collective/icalendar/pull/100
|
||||
"""
|
||||
|
||||
ical_content = "BEGIN:VEVENT\r\nSUMMARY;LANGUAGE=ru:te\r\nEND:VEVENT"
|
||||
icalendar.Event.from_ical(ical_content).to_ical()
|
||||
|
||||
def test_issue_101(self):
|
||||
"""Issue #101 - icalendar is choking on umlauts in ORGANIZER
|
||||
|
||||
https://github.com/collective/icalendar/issues/101
|
||||
"""
|
||||
ical_str = r"""BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
X-WR-CALNAME:Kalender von acme\, admin
|
||||
PRODID:-//The Horde Project//Horde_iCalendar Library\, Horde 3.3.5//EN
|
||||
METHOD:PUBLISH
|
||||
BEGIN:VEVENT
|
||||
DTSTART:20130416T100000Z
|
||||
DTEND:20130416T110000Z
|
||||
DTSTAMP:20130416T092616Z
|
||||
UID:20130416112341.10064jz0k4j7uem8@acmenet.de
|
||||
CREATED:20130416T092341Z
|
||||
LAST-MODIFIED:20130416T092341Z
|
||||
SUMMARY:wichtiger termin 1
|
||||
ORGANIZER;CN="acme, ädmin":mailto:adm-acme@mydomain.de
|
||||
LOCATION:im büro
|
||||
CLASS:PUBLIC
|
||||
STATUS:CONFIRMED
|
||||
TRANSP:OPAQUE
|
||||
END:VEVENT
|
||||
END:VCALENDAR"""
|
||||
|
||||
cal = icalendar.Calendar.from_ical(ical_str)
|
||||
org_cn = cal.walk('VEVENT')[0]['ORGANIZER'].params['CN']
|
||||
self.assertEqual(org_cn, 'acme, ädmin')
|
||||
|
||||
def test_issue_104__ignore_exceptions(self):
|
||||
"""
|
||||
Issue #104 - line parsing error in a VEVENT
|
||||
(which has ignore_exceptions). Should mark the event broken
|
||||
but not raise an exception.
|
||||
https://github.com/collective/icalendar/issues/104
|
||||
"""
|
||||
ical_str = """
|
||||
BEGIN:VEVENT
|
||||
DTSTART:20140401T000000Z
|
||||
DTEND:20140401T010000Z
|
||||
DTSTAMP:20140401T000000Z
|
||||
SUMMARY:Broken Eevnt
|
||||
CLASS:PUBLIC
|
||||
STATUS:CONFIRMED
|
||||
TRANSP:OPAQUE
|
||||
X
|
||||
END:VEVENT"""
|
||||
event = icalendar.Calendar.from_ical(ical_str)
|
||||
self.assertTrue(isinstance(event, icalendar.Event))
|
||||
self.assertTrue(event.is_broken) # REMOVE FOR NEXT MAJOR RELEASE
|
||||
self.assertEqual(
|
||||
event.errors,
|
||||
[(None, "Content line could not be parsed into parts: 'X': Invalid content line")] # noqa
|
||||
)
|
||||
|
||||
def test_issue_104__no_ignore_exceptions(self):
|
||||
"""
|
||||
Issue #104 - line parsing error in a VCALENDAR
|
||||
(which doesn't have ignore_exceptions). Should raise an exception.
|
||||
"""
|
||||
ical_str = """BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
BEGIN:VEVENT
|
||||
DTSTART:20140401T000000Z
|
||||
DTEND:20140401T010000Z
|
||||
DTSTAMP:20140401T000000Z
|
||||
SUMMARY:Broken Eevnt
|
||||
CLASS:PUBLIC
|
||||
STATUS:CONFIRMED
|
||||
TRANSP:OPAQUE
|
||||
END:VEVENT
|
||||
X
|
||||
END:VCALENDAR"""
|
||||
with self.assertRaises(ValueError):
|
||||
icalendar.Calendar.from_ical(ical_str)
|
||||
|
||||
def test_issue_112(self):
|
||||
"""Issue #112 - No timezone info on EXDATE
|
||||
https://github.com/collective/icalendar/issues/112
|
||||
"""
|
||||
directory = os.path.dirname(__file__)
|
||||
path = os.path.join(directory,
|
||||
'issue_112_missing_tzinfo_on_exdate.ics')
|
||||
with open(path, 'rb') as ics:
|
||||
cal = icalendar.Calendar.from_ical(ics.read())
|
||||
event = cal.walk('VEVENT')[0]
|
||||
|
||||
event_ical = to_unicode(event.to_ical()) # Py3 str type doesn't
|
||||
# support buffer API
|
||||
# General timezone aware dates in ical string
|
||||
self.assertTrue('DTSTART;TZID=America/New_York:20130907T120000'
|
||||
in event_ical)
|
||||
self.assertTrue('DTEND;TZID=America/New_York:20130907T170000'
|
||||
in event_ical)
|
||||
# Specific timezone aware exdates in ical string
|
||||
self.assertTrue('EXDATE;TZID=America/New_York:20131012T120000'
|
||||
in event_ical)
|
||||
self.assertTrue('EXDATE;TZID=America/New_York:20131011T120000'
|
||||
in event_ical)
|
||||
|
||||
self.assertEqual(event['exdate'][0].dts[0].dt.tzname(), 'EDT')
|
||||
|
||||
def test_issue_116(self):
|
||||
"""Issue #116/#117 - How to add 'X-APPLE-STRUCTURED-LOCATION'
|
||||
https://github.com/collective/icalendar/issues/116
|
||||
|
@ -289,70 +61,6 @@ END:VCALENDAR"""
|
|||
icalendar.Event.from_ical(event.to_ical()).to_ical()
|
||||
)
|
||||
|
||||
def test_issue_142(self):
|
||||
"""Issue #142 - Multivalued parameters
|
||||
This is needed for VCard 3.0.
|
||||
https://github.com/collective/icalendar/pull/142
|
||||
"""
|
||||
from icalendar.parser import Contentline, Parameters
|
||||
|
||||
ctl = Contentline.from_ical("TEL;TYPE=HOME,VOICE:000000000")
|
||||
|
||||
self.assertEqual(
|
||||
ctl.parts(),
|
||||
('TEL', Parameters({'TYPE': ['HOME', 'VOICE']}), '000000000'),
|
||||
)
|
||||
|
||||
def test_issue_143(self):
|
||||
"""Issue #143 - Allow dots in property names.
|
||||
Another vCard related issue.
|
||||
https://github.com/collective/icalendar/pull/143
|
||||
"""
|
||||
from icalendar.parser import Contentline, Parameters
|
||||
|
||||
ctl = Contentline.from_ical("ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR:;;This is the Adress 08; Some City;;12345;Germany") # nopep8
|
||||
self.assertEqual(
|
||||
ctl.parts(),
|
||||
('ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR',
|
||||
Parameters(),
|
||||
';;This is the Adress 08; Some City;;12345;Germany'),
|
||||
)
|
||||
|
||||
ctl2 = Contentline.from_ical("ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL:") # nopep8
|
||||
self.assertEqual(
|
||||
ctl2.parts(),
|
||||
('ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL',
|
||||
Parameters(),
|
||||
''),
|
||||
)
|
||||
|
||||
def test_issue_157(self):
|
||||
"""Issue #157 - Recurring rules and trailing semicolons
|
||||
https://github.com/collective/icalendar/pull/157
|
||||
"""
|
||||
# The trailing semicolon caused a problem
|
||||
ical_str = """BEGIN:VEVENT
|
||||
DTSTART:20150325T101010
|
||||
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU;
|
||||
END:VEVENT"""
|
||||
|
||||
cal = icalendar.Calendar.from_ical(ical_str)
|
||||
recur = cal.decoded("RRULE")
|
||||
self.assertIsInstance(recur, icalendar.vRecur)
|
||||
self.assertEqual(
|
||||
recur.to_ical(),
|
||||
b'FREQ=YEARLY;BYDAY=1SU;BYMONTH=11'
|
||||
)
|
||||
|
||||
def test_index_error_issue(self):
|
||||
"""Found an issue where from_ical() would raise IndexError for
|
||||
properties without parent components.
|
||||
https://github.com/collective/icalendar/pull/179
|
||||
"""
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
icalendar.Calendar.from_ical('VERSION:2.0')
|
||||
|
||||
def test_issue_178(self):
|
||||
"""Issue #178 - A component with an unknown/invalid name is represented
|
||||
as one of the known components, the information about the original
|
||||
|
@ -395,26 +103,6 @@ END:VEVENT"""
|
|||
b'BEGIN:VEVENT\r\nDTSTART:20150122\r\nUID:12345\r\n'
|
||||
b'END:VEVENT\r\nEND:MYCOMPTOO\r\n')
|
||||
|
||||
def test_issue_184(self):
|
||||
"""Issue #184 - Previous changes in code broke already broken
|
||||
representation of PERIOD values - in a new way"""
|
||||
|
||||
ical_str = ['BEGIN:VEVENT',
|
||||
'DTSTAMP:20150219T133000',
|
||||
'DTSTART:20150219T133000',
|
||||
'UID:1234567',
|
||||
'RDATE;VALUE=PERIOD:20150219T133000/PT10H',
|
||||
'END:VEVENT']
|
||||
|
||||
event = icalendar.Event.from_ical('\r\n'.join(ical_str))
|
||||
self.assertEqual(event.errors, [])
|
||||
self.assertEqual(event.to_ical(),
|
||||
b'BEGIN:VEVENT\r\nDTSTART:20150219T133000\r\n'
|
||||
b'DTSTAMP:20150219T133000\r\nUID:1234567\r\n'
|
||||
b'RDATE;VALUE=PERIOD:20150219T133000/PT10H\r\n'
|
||||
b'END:VEVENT\r\n'
|
||||
)
|
||||
|
||||
def test_issue_237(self):
|
||||
"""Issue #237 - Fail to parse timezone with non-ascii TZID"""
|
||||
|
||||
|
@ -458,27 +146,3 @@ END:VEVENT"""
|
|||
expected_tzname = 'Brasília standard'.encode('ascii', 'replace')
|
||||
self.assertEqual(dtstart.tzinfo.zone, expected_zone)
|
||||
self.assertEqual(dtstart.tzname(), expected_tzname)
|
||||
|
||||
def test_issue_345(self):
|
||||
"""Issue #345 - Why is tools.UIDGenerator a class (that must be instantiated) instead of a module? """
|
||||
uid1 = icalendar.tools.UIDGenerator.uid()
|
||||
uid2 = icalendar.tools.UIDGenerator.uid('test.test')
|
||||
uid3 = icalendar.tools.UIDGenerator.uid(unique='123')
|
||||
uid4 = icalendar.tools.UIDGenerator.uid('test.test', '123')
|
||||
|
||||
self.assertEqual(uid1.split('@')[1], 'example.com')
|
||||
self.assertEqual(uid2.split('@')[1], 'test.test')
|
||||
self.assertEqual(uid3.split('-')[1], '123@example.com')
|
||||
self.assertEqual(uid4.split('-')[1], '123@test.test')
|
||||
|
||||
@pytest.mark.parametrize("zone", [
|
||||
pytz.utc,
|
||||
zoneinfo.ZoneInfo('UTC'),
|
||||
pytz.timezone('UTC'),
|
||||
tz.UTC,
|
||||
tz.gettz('UTC')])
|
||||
def test_issue_335_identify_UTC(zone):
|
||||
myevent = icalendar.Event()
|
||||
dt = datetime.datetime(2021, 11, 17, 15, 9, 15)
|
||||
myevent.add('dtstart', dt.astimezone(zone))
|
||||
assert 'DTSTART;VALUE=DATE-TIME:20211117T150915Z' in myevent.to_ical().decode('ASCII')
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import pytest
|
||||
|
||||
from icalendar import Event, Calendar
|
||||
|
||||
def test_ignore_exceptions_on_broken_events_issue_104(events):
|
||||
''' Issue #104 - line parsing error in a VEVENT
|
||||
(which has ignore_exceptions). Should mark the event broken
|
||||
but not raise an exception.
|
||||
|
||||
https://github.com/collective/icalendar/issues/104
|
||||
'''
|
||||
assert events.issue_104_mark_events_broken.is_broken # TODO: REMOVE FOR NEXT MAJOR RELEASE
|
||||
assert events.issue_104_mark_events_broken.errors == [(None, "Content line could not be parsed into parts: 'X': Invalid content line")]
|
||||
|
||||
def test_dont_ignore_exceptions_on_broken_calendars_issue_104(calendars):
|
||||
'''Issue #104 - line parsing error in a VCALENDAR
|
||||
(which doesn't have ignore_exceptions). Should raise an exception.
|
||||
'''
|
||||
with pytest.raises(ValueError):
|
||||
calendars.issue_104_broken_calendar
|
|
@ -0,0 +1,121 @@
|
|||
'''Tests checking that parsing works'''
|
||||
import pytest
|
||||
from icalendar import Calendar, vRecur, vBinary, Event
|
||||
from datetime import datetime
|
||||
from icalendar.parser import Contentline, Parameters
|
||||
|
||||
|
||||
@pytest.mark.parametrize('raw_content_line, expected_output', [
|
||||
# Issue #142 - Multivalued parameters. This is needed for VCard 3.0.
|
||||
# see https://github.com/collective/icalendar/pull/142
|
||||
('TEL;TYPE=HOME,VOICE:000000000', ('TEL', Parameters({'TYPE': ['HOME', 'VOICE']}), '000000000')),
|
||||
# Issue #143 - Allow dots in property names. Another vCard related issue.
|
||||
# see https://github.com/collective/icalendar/pull/143
|
||||
('ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR:;;This is the Adress 08; Some City;;12345;Germany', \
|
||||
('ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR', \
|
||||
Parameters(),\
|
||||
';;This is the Adress 08; Some City;;12345;Germany')),
|
||||
('ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL:', \
|
||||
('ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL', \
|
||||
Parameters(), \
|
||||
''))
|
||||
])
|
||||
def test_content_lines_parsed_properly(raw_content_line, expected_output):
|
||||
assert Contentline.from_ical(raw_content_line).parts() == expected_output
|
||||
|
||||
|
||||
@pytest.mark.parametrize('timezone_info', [
|
||||
# General timezone aware dates in ical string
|
||||
(b'DTSTART;TZID=America/New_York:20130907T120000'),
|
||||
(b'DTEND;TZID=America/New_York:20130907T170000'),
|
||||
# Specific timezone aware exdates in ical string
|
||||
(b'EXDATE;TZID=America/New_York:20131012T120000'),
|
||||
(b'EXDATE;TZID=America/New_York:20131011T120000')
|
||||
])
|
||||
def test_timezone_info_present_in_ical_issue_112(events, timezone_info):
|
||||
'''Issue #112 - No timezone info on EXDATE
|
||||
|
||||
https://github.com/collective/icalendar/issues/112
|
||||
'''
|
||||
timezone_info in events.issue_112_missing_tzinfo_on_exdate.to_ical()
|
||||
|
||||
def test_timezone_name_parsed_issue_112(events):
|
||||
'''Issue #112 - No timezone info on EXDATE
|
||||
|
||||
https://github.com/collective/icalendar/issues/112
|
||||
'''
|
||||
assert events.issue_112_missing_tzinfo_on_exdate['exdate'][0].dts[0].dt.tzname() == 'EDT'
|
||||
|
||||
def test_issue_157_removes_trailing_semicolon(events):
|
||||
'''Issue #157 - Recurring rules and trailing semicolons
|
||||
|
||||
https://github.com/collective/icalendar/pull/157
|
||||
'''
|
||||
recur = events.issue_157_removes_trailing_semicolon.decoded("RRULE")
|
||||
assert isinstance(recur, vRecur)
|
||||
assert recur.to_ical() == b'FREQ=YEARLY;BYDAY=1SU;BYMONTH=11'
|
||||
|
||||
@pytest.mark.parametrize('event_name', [
|
||||
# https://github.com/collective/icalendar/pull/100
|
||||
('issue_100_transformed_doctests_into_unittests'),
|
||||
('issue_184_broken_representation_of_period'),
|
||||
])
|
||||
def test_event_to_ical_is_inverse_of_from_ical(events, event_name):
|
||||
"""Make sure that an event's ICS is equal to the ICS it was made from."""
|
||||
event = events[event_name]
|
||||
assert event.to_ical() == event.raw_ics
|
||||
|
||||
def test_decode_rrule_attribute_error_issue_70(events):
|
||||
# Issue #70 - e.decode("RRULE") causes Attribute Error
|
||||
# see https://github.com/collective/icalendar/issues/70
|
||||
recur = events.issue_70_rrule_causes_attribute_error.decoded('RRULE')
|
||||
assert isinstance(recur, vRecur)
|
||||
assert recur.to_ical() == b'FREQ=WEEKLY;UNTIL=20070619T225959;INTERVAL=1'
|
||||
|
||||
def test_description_parsed_properly_issue_53(events):
|
||||
'''Issue #53 - Parsing failure on some descriptions?
|
||||
|
||||
https://github.com/collective/icalendar/issues/53
|
||||
'''
|
||||
assert b'July 12 at 6:30 PM' in events.issue_53_description_parsed_properly['DESCRIPTION'].to_ical()
|
||||
|
||||
def test_raises_value_error_for_properties_without_parent_pull_179():
|
||||
'''Found an issue where from_ical() would raise IndexError for
|
||||
properties without parent components.
|
||||
|
||||
https://github.com/collective/icalendar/pull/179
|
||||
'''
|
||||
with pytest.raises(ValueError):
|
||||
Calendar.from_ical('VERSION:2.0')
|
||||
|
||||
def test_tzid_parsed_properly_issue_53(timezones):
|
||||
'''Issue #53 - Parsing failure on some descriptions?
|
||||
|
||||
https://github.com/collective/icalendar/issues/53
|
||||
'''
|
||||
assert timezones.issue_53_tzid_parsed_properly['tzid'].to_ical() == b'America/New_York'
|
||||
|
||||
def test_timezones_to_ical_is_inverse_of_from_ical(timezones):
|
||||
'''Issue #55 - Parse error on utc-offset with seconds value
|
||||
see https://github.com/collective/icalendar/issues/55'''
|
||||
timezone = timezones['issue_55_parse_error_on_utc_offset_with_seconds']
|
||||
assert timezone.to_ical() == timezone.raw_ics
|
||||
|
||||
@pytest.mark.parametrize('date, expected_output', [
|
||||
(datetime(2012, 7, 16, 0, 0, 0), b'DTSTART;VALUE=DATE-TIME:20120716T000000Z'),
|
||||
(datetime(2021, 11, 17, 15, 9, 15), b'DTSTART;VALUE=DATE-TIME:20211117T150915Z')
|
||||
])
|
||||
def test_no_tzid_when_utc(utc, date, expected_output):
|
||||
'''Issue #58 - TZID on UTC DATE-TIMEs
|
||||
Issue #335 - UTC timezone identification is broken
|
||||
|
||||
https://github.com/collective/icalendar/issues/58
|
||||
https://github.com/collective/icalendar/issues/335
|
||||
'''
|
||||
# According to RFC 2445: "The TZID property parameter MUST NOT be
|
||||
# applied to DATE-TIME or TIME properties whose time values are
|
||||
# specified in UTC.
|
||||
date = date.replace(tzinfo=utc)
|
||||
event = Event()
|
||||
event.add('dtstart', date)
|
||||
assert expected_output in event.to_ical()
|
|
@ -1,6 +1,7 @@
|
|||
import pytest
|
||||
import unittest
|
||||
from icalendar.tools import UIDGenerator
|
||||
|
||||
from icalendar.tools import UIDGenerator
|
||||
|
||||
class TestTools(unittest.TestCase):
|
||||
|
||||
|
@ -26,3 +27,28 @@ class TestTools(unittest.TestCase):
|
|||
txt = uid.to_ical()
|
||||
self.assertTrue(len(txt) == length)
|
||||
self.assertTrue(b'-/path/to/content@Example.ORG' in txt)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('split,expected,args,kw', [
|
||||
# default argument host_name
|
||||
("@", "example.com", (), {},),
|
||||
("@", "example.com", ("example.com",), {}),
|
||||
("@", "example.com", (), {"host_name":"example.com"}),
|
||||
# replaced host_name
|
||||
("@", "test.test", ("test.test",), {}),
|
||||
("@", "test.test", (), {"host_name":"test.test"}),
|
||||
# replace unique
|
||||
("-", "123@example.com", (), {"unique": "123"},),
|
||||
("-", "abc@example.com", (), {"unique": "abc"},),
|
||||
# replace host_name and unique
|
||||
("-", "1234@test.icalendar", (), {"unique": "1234", "host_name":"test.icalendar"},),
|
||||
("-", "abc@test.example.com", ("test.example.com", "abc"), {},),
|
||||
|
||||
])
|
||||
def test_uid_generator_issue_345(args, kw, split, expected):
|
||||
'''Issue #345 - Why is tools.UIDGenerator a class (that must be instantiated) instead of a module?
|
||||
|
||||
see https://github.com/collective/icalendar/issues/345
|
||||
'''
|
||||
uid = UIDGenerator.uid(*args, **kw)
|
||||
assert uid.split(split)[1] == expected
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
BEGIN:VTIMEZONE
|
||||
TZID:America/New_York
|
||||
TZURL:http://tzurl.org/zoneinfo-outlook/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
|
|
@ -0,0 +1,10 @@
|
|||
BEGIN:VTIMEZONE
|
||||
TZID:America/Los Angeles
|
||||
BEGIN:STANDARD
|
||||
DTSTART:18831118T120702
|
||||
RDATE:18831118T120702
|
||||
TZNAME:PST
|
||||
TZOFFSETFROM:-075258
|
||||
TZOFFSETTO:-0800
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
Ładowanie…
Reference in New Issue