Ruff format tests (#719)

* ruff format tests

* log changes

* create missing pytz.UnknownTimeZoneError

---------

Co-authored-by: Steve Piercy <web@stevepiercy.com>
pull/734/head
Nicco Kunzmann 2024-10-16 12:34:09 +01:00 zatwierdzone przez GitHub
rodzic 48d471eb29
commit 6efa2d3bc9
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
48 zmienionych plików z 1929 dodań i 1354 usunięć

Wyświetl plik

@ -6,6 +6,7 @@ Changelog
Minor changes:
- Format test code with Ruff. See `Issue 672 <https://github.com/collective/icalendar/issues/672>`_.
- Document the Debian package. See `Issue 701 <https://github.com/collective/icalendar/issues/701>`_.
Breaking changes:

Wyświetl plik

@ -3,25 +3,28 @@ try:
except ImportError:
import zoneinfo
import pytest
import icalendar
try:
import pytz
except ImportError:
pytz = None
from datetime import datetime
from dateutil import tz
from icalendar.cal import Component, Calendar
from icalendar.timezone import tzp as _tzp
from icalendar.timezone import TZP
from pathlib import Path
import itertools
import sys
from pathlib import Path
from dateutil import tz
from icalendar.cal import Calendar, Component
from icalendar.timezone import TZP
from icalendar.timezone import tzp as _tzp
HAS_PYTZ = pytz is not None
if HAS_PYTZ:
PYTZ_UTC = [
pytz.utc,
pytz.timezone('UTC'),
pytz.timezone("UTC"),
]
PYTZ_IN_TIMEZONE = [
lambda dt, tzname: pytz.timezone(tzname).localize(dt),
@ -34,14 +37,19 @@ else:
class DataSource:
'''A collection of parsed ICS elements (e.g calendars, timezones, events)'''
def __init__(self, data_source_folder:Path, parser):
"""A collection of parsed ICS elements (e.g calendars, timezones, events)"""
def __init__(self, data_source_folder: Path, parser):
self._parser = parser
self._data_source_folder = data_source_folder
def keys(self):
"""Return all the files that could be used."""
return [p.stem for p in self._data_source_folder.iterdir() if p.suffix.lower() == ".ics"]
return [
p.stem
for p in self._data_source_folder.iterdir()
if p.suffix.lower() == ".ics"
]
def __getitem__(self, attribute):
"""Parse a file and return the result stored in the attribute."""
@ -49,11 +57,11 @@ class DataSource:
source_file = attribute
attribute = attribute[:-4]
else:
source_file = attribute + '.ics'
source_file = attribute + ".ics"
source_path = self._data_source_folder / source_file
if not source_path.is_file():
raise AttributeError(f"{source_path} does not exist.")
with source_path.open('rb') as f:
with source_path.open("rb") as f:
raw_ics = f.read()
source = self._parser(raw_ics)
if not isinstance(source, list):
@ -76,51 +84,67 @@ class DataSource:
@property
def multiple(self):
"""Return a list of all components parsed."""
return self.__class__(self._data_source_folder, lambda data: self._parser(data, multiple=True))
return self.__class__(
self._data_source_folder, lambda data: self._parser(data, multiple=True)
)
HERE = Path(__file__).parent
CALENDARS_FOLDER = HERE / 'calendars'
TIMEZONES_FOLDER = HERE / 'timezones'
EVENTS_FOLDER = HERE / 'events'
CALENDARS_FOLDER = HERE / "calendars"
TIMEZONES_FOLDER = HERE / "timezones"
EVENTS_FOLDER = HERE / "events"
@pytest.fixture(scope="module")
def calendars(tzp):
return DataSource(CALENDARS_FOLDER, icalendar.Calendar.from_ical)
@pytest.fixture(scope="module")
def timezones(tzp):
return DataSource(TIMEZONES_FOLDER, icalendar.Timezone.from_ical)
@pytest.fixture(scope="module")
def events(tzp):
return DataSource(EVENTS_FOLDER, icalendar.Event.from_ical)
@pytest.fixture(params=PYTZ_UTC + [
zoneinfo.ZoneInfo('UTC'),
tz.UTC,
tz.gettz('UTC')])
@pytest.fixture(params=PYTZ_UTC + [zoneinfo.ZoneInfo("UTC"), tz.UTC, tz.gettz("UTC")])
def utc(request, tzp):
return request.param
@pytest.fixture(params=PYTZ_IN_TIMEZONE + [
lambda dt, tzname: dt.replace(tzinfo=tz.gettz(tzname)),
lambda dt, tzname: dt.replace(tzinfo=zoneinfo.ZoneInfo(tzname))
])
@pytest.fixture(
params=PYTZ_IN_TIMEZONE
+ [
lambda dt, tzname: dt.replace(tzinfo=tz.gettz(tzname)),
lambda dt, tzname: dt.replace(tzinfo=zoneinfo.ZoneInfo(tzname)),
]
)
def in_timezone(request, tzp):
return request.param
# exclude broken calendars here
ICS_FILES_EXCLUDE = (
"big_bad_calendar.ics", "issue_104_broken_calendar.ics", "small_bad_calendar.ics",
"multiple_calendar_components.ics", "pr_480_summary_with_colon.ics",
"parsing_error_in_UTC_offset.ics", "parsing_error.ics",
"big_bad_calendar.ics",
"issue_104_broken_calendar.ics",
"small_bad_calendar.ics",
"multiple_calendar_components.ics",
"pr_480_summary_with_colon.ics",
"parsing_error_in_UTC_offset.ics",
"parsing_error.ics",
)
ICS_FILES = [
file.name for file in
itertools.chain(CALENDARS_FOLDER.iterdir(), TIMEZONES_FOLDER.iterdir(), EVENTS_FOLDER.iterdir())
file.name
for file in itertools.chain(
CALENDARS_FOLDER.iterdir(), TIMEZONES_FOLDER.iterdir(), EVENTS_FOLDER.iterdir()
)
if file.name not in ICS_FILES_EXCLUDE
]
@pytest.fixture(params=ICS_FILES)
def ics_file(tzp, calendars, timezones, events, request):
"""An example ICS file."""
@ -133,71 +157,76 @@ def ics_file(tzp, calendars, timezones, events, request):
FUZZ_V1 = [key for key in CALENDARS_FOLDER.iterdir() if "fuzz-testcase" in str(key)]
@pytest.fixture(params=FUZZ_V1)
def fuzz_v1_calendar(request):
"""Clusterfuzz calendars."""
return request.param
@pytest.fixture()
@pytest.fixture
def x_sometime():
"""Map x_sometime to time"""
icalendar.cal.types_factory.types_map['X-SOMETIME'] = 'time'
icalendar.cal.types_factory.types_map["X-SOMETIME"] = "time"
yield
icalendar.cal.types_factory.types_map.pop('X-SOMETIME')
icalendar.cal.types_factory.types_map.pop("X-SOMETIME")
@pytest.fixture()
@pytest.fixture
def factory():
"""Return a new component factory."""
return icalendar.ComponentFactory()
@pytest.fixture()
@pytest.fixture
def vUTCOffset_ignore_exceptions():
icalendar.vUTCOffset.ignore_exceptions = True
yield
icalendar.vUTCOffset.ignore_exceptions = False
@pytest.fixture()
@pytest.fixture
def event_component(tzp):
"""Return an event component."""
c = Component()
c.name = 'VEVENT'
c.name = "VEVENT"
return c
@pytest.fixture()
@pytest.fixture
def c(tzp):
"""Return an empty component."""
c = Component()
return c
comp = c
@pytest.fixture()
@pytest.fixture
def calendar_component(tzp):
"""Return an empty component."""
c = Component()
c.name = 'VCALENDAR'
c.name = "VCALENDAR"
return c
@pytest.fixture()
@pytest.fixture
def filled_event_component(c, calendar_component):
"""Return an event with some values and add it to calendar_component."""
e = Component(summary='A brief history of time')
e.name = 'VEVENT'
e.add('dtend', '20000102T000000', encode=0)
e.add('dtstart', '20000101T000000', encode=0)
e = Component(summary="A brief history of time")
e.name = "VEVENT"
e.add("dtend", "20000102T000000", encode=0)
e.add("dtstart", "20000101T000000", encode=0)
calendar_component.add_component(e)
return e
@pytest.fixture()
@pytest.fixture
def calendar_with_resources(tzp):
c = Calendar()
c['resources'] = 'Chair, Table, "Room: 42"'
c["resources"] = 'Chair, Table, "Room: 42"'
return c
@ -220,13 +249,13 @@ def other_tzp(request, tzp):
return tzp
@pytest.fixture()
@pytest.fixture
def pytz_only(tzp):
"""Skip tests that are not running under pytz."""
assert tzp.uses_pytz()
@pytest.fixture()
@pytest.fixture
def zoneinfo_only(tzp, request, tzp_name):
"""Skip tests that are not running under zoneinfo."""
assert tzp.uses_zoneinfo()
@ -247,14 +276,18 @@ def pytest_generate_tests(metafunc):
tzp_names = ["zoneinfo"]
if "pytz_only" in metafunc.fixturenames:
tzp_names = PYTZ_TZP
assert not ("zoneinfo_only" in metafunc.fixturenames and "pytz_only" in metafunc.fixturenames), "Use pytz_only or zoneinfo_only but not both!"
assert not (
"zoneinfo_only" in metafunc.fixturenames
and "pytz_only" in metafunc.fixturenames
), "Use pytz_only or zoneinfo_only but not both!"
metafunc.parametrize("tzp_name", tzp_names, scope="module")
class DoctestZoneInfo(zoneinfo.ZoneInfo):
"""Constent ZoneInfo representation for tests."""
def __repr__(self):
return f"ZoneInfo(key={repr(self.key)})"
return f"ZoneInfo(key={self.key!r})"
def doctest_print(obj):
@ -270,13 +303,13 @@ def doctest_import(name, *args, **kw):
return pytz
return __import__(name, *args, **kw)
@pytest.fixture()
@pytest.fixture
def env_for_doctest(monkeypatch):
"""Modify the environment to make doctests run."""
monkeypatch.setitem(sys.modules, "zoneinfo", zoneinfo)
monkeypatch.setattr(zoneinfo, "ZoneInfo", DoctestZoneInfo)
from icalendar.timezone.zoneinfo import ZONEINFO
monkeypatch.setattr(ZONEINFO, "utc", zoneinfo.ZoneInfo("UTC"))
return {
"print": doctest_print
}
return {"print": doctest_print}

Wyświetl plik

@ -5,7 +5,10 @@ These test cases reproduce the failure.
Some more tests can be added to make sure that the behavior works properly.
"""
def fuzz_calendar_v1(from_ical, calendar_string: str, multiple: bool, should_walk: bool):
def fuzz_calendar_v1(
from_ical, calendar_string: str, multiple: bool, should_walk: bool
):
"""Take a from_ical function and reproduce the error.
The calendar_string is a fuzzed input.
@ -16,7 +19,7 @@ def fuzz_calendar_v1(from_ical, calendar_string: str, multiple: bool, should_wal
cal = [cal]
for c in cal:
if should_walk:
for event in cal.walk('VEVENT'):
for event in cal.walk("VEVENT"):
event.to_ical()
else:
cal.to_ical()

Wyświetl plik

@ -1,13 +1,12 @@
"""This test tests all fuzzed calendars."""
from icalendar.tests.fuzzed import fuzz_calendar_v1
import icalendar
from icalendar.tests.fuzzed import fuzz_calendar_v1
def test_fuzz_v1(fuzz_v1_calendar):
"""Test a calendar."""
with open(fuzz_v1_calendar, "rb") as f:
fuzz_calendar_v1(
icalendar.Calendar.from_ical,
f.read(),
multiple=True,
should_walk=True
icalendar.Calendar.from_ical, f.read(), multiple=True, should_walk=True
)

Wyświetl plik

@ -1,28 +1,24 @@
import string
import unittest
from hypothesis import given, settings
import hypothesis.strategies as st
from hypothesis import given, settings
from icalendar.parser import Contentline, Contentlines, Parameters
import unittest
def printable_characters(**kw):
return st.text(
st.characters(blacklist_categories=(
'Cc', 'Cs'
), **kw)
)
return st.text(st.characters(blacklist_categories=("Cc", "Cs"), **kw))
key = st.text(string.ascii_letters + string.digits, min_size=1)
value = printable_characters(blacklist_characters='\\;:\"')
value = printable_characters(blacklist_characters='\\;:"')
class TestFuzzing(unittest.TestCase):
@given(lines=st.lists(
st.tuples(key, st.dictionaries(key, value), value),
min_size=1
))
@given(
lines=st.lists(st.tuples(key, st.dictionaries(key, value), value), min_size=1)
)
@settings(max_examples=10**3)
def test_main(self, lines):
cl = Contentlines()
@ -33,6 +29,6 @@ class TestFuzzing(unittest.TestCase):
# Happens when there is a random parameter 'self'...
continue
cl.append(Contentline.from_parts(key, params, value))
cl.append('')
cl.append("")
assert Contentlines.from_ical(cl.to_ical()) == cl

Wyświetl plik

@ -1,47 +1,70 @@
"""Test the identity and equality between properties."""
from datetime import date, datetime, time
from icalendar import vDDDTypes
from datetime import datetime, date, time
from icalendar.timezone.zoneinfo import zoneinfo
try:
import pytz
except ImportError:
pytz = None
from dateutil import tz
import pytest
from copy import deepcopy
import pytest
from dateutil import tz
vDDDTypes_list = [
vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7, tzinfo=zoneinfo.ZoneInfo("Europe/London"))),
vDDDTypes(
datetime(
year=2022,
month=7,
day=22,
hour=12,
minute=7,
tzinfo=zoneinfo.ZoneInfo("Europe/London"),
)
),
vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7)),
vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7, tzinfo=tz.UTC)),
vDDDTypes(date(year=2022, month=7, day=22)),
vDDDTypes(date(year=2022, month=7, day=23)),
vDDDTypes(time(hour=22, minute=7, second=2))
vDDDTypes(time(hour=22, minute=7, second=2)),
]
if pytz:
vDDDTypes_list.append(vDDDTypes(pytz.timezone('EST').localize(datetime(year=2022, month=7, day=22, hour=12, minute=7))),)
vDDDTypes_list.append(
vDDDTypes(
pytz.timezone("EST").localize(
datetime(year=2022, month=7, day=22, hour=12, minute=7)
)
),
)
def identity(x):
return x
@pytest.mark.parametrize("map", [
deepcopy,
identity,
hash,
])
@pytest.mark.parametrize(
"map",
[
deepcopy,
identity,
hash,
],
)
@pytest.mark.parametrize("v_type", vDDDTypes_list)
@pytest.mark.parametrize("other", vDDDTypes_list)
def test_vDDDTypes_equivalance(map, v_type, other):
if v_type is other:
assert map(v_type) == map(other), f"identity implies equality: {map.__name__}()"
assert not (map(v_type) != map(other)), f"identity implies equality: {map.__name__}()"
assert map(v_type) == map(other), f"identity implies equality: {map.__name__}()"
else:
assert map(v_type) != map(other), f"expected inequality: {map.__name__}()"
assert not (map(v_type) == map(other)), f"expected inequality: {map.__name__}()"
assert map(v_type) != map(other), f"expected inequality: {map.__name__}()"
@pytest.mark.parametrize("v_type", vDDDTypes_list)
def test_inequality_with_different_types(v_type):
assert v_type != 42
assert v_type != 'test'
assert v_type != "test"

Wyświetl plik

@ -1,17 +1,19 @@
"""Test that composed values are properly converted."""
from icalendar import Event
from datetime import datetime
from icalendar import Event
def test_vDDDLists_timezone(tzp):
"""Test vDDDLists with timezone information."""
vevent = Event()
dt1 = tzp.localize(datetime(2013, 1, 1), 'Europe/Vienna')
dt2 = tzp.localize(datetime(2013, 1, 2), 'Europe/Vienna')
dt3 = tzp.localize(datetime(2013, 1, 3), 'Europe/Vienna')
vevent.add('rdate', [dt1, dt2])
vevent.add('exdate', dt3)
dt1 = tzp.localize(datetime(2013, 1, 1), "Europe/Vienna")
dt2 = tzp.localize(datetime(2013, 1, 2), "Europe/Vienna")
dt3 = tzp.localize(datetime(2013, 1, 3), "Europe/Vienna")
vevent.add("rdate", [dt1, dt2])
vevent.add("exdate", dt3)
ical = vevent.to_ical()
assert b'RDATE;TZID=Europe/Vienna:20130101T000000,20130102T000000' in ical
assert b'EXDATE;TZID=Europe/Vienna:20130103T000000' in ical
assert b"RDATE;TZID=Europe/Vienna:20130101T000000,20130102T000000" in ical
assert b"EXDATE;TZID=Europe/Vienna:20130103T000000" in ical

Wyświetl plik

@ -1,118 +1,112 @@
from datetime import date
from datetime import datetime
from datetime import time
from datetime import timedelta
from icalendar.parser import Parameters
import unittest
from datetime import date, datetime, time, timedelta
from icalendar.parser import Parameters
class TestProp(unittest.TestCase):
def test_prop_vFloat(self):
from icalendar.prop import vFloat
self.assertEqual(vFloat(1.0).to_ical(), b'1.0')
self.assertEqual(vFloat.from_ical('42'), 42.0)
self.assertEqual(vFloat(42).to_ical(), b'42.0')
self.assertRaises(ValueError, vFloat.from_ical, '1s3')
self.assertEqual(vFloat(1.0).to_ical(), b"1.0")
self.assertEqual(vFloat.from_ical("42"), 42.0)
self.assertEqual(vFloat(42).to_ical(), b"42.0")
self.assertRaises(ValueError, vFloat.from_ical, "1s3")
def test_prop_vInt(self):
from icalendar.prop import vInt
self.assertEqual(vInt(42).to_ical(), b'42')
self.assertEqual(vInt.from_ical('13'), 13)
self.assertRaises(ValueError, vInt.from_ical, '1s3')
self.assertEqual(vInt(42).to_ical(), b"42")
self.assertEqual(vInt.from_ical("13"), 13)
self.assertRaises(ValueError, vInt.from_ical, "1s3")
def test_prop_vDDDLists(self):
from icalendar.prop import vDDDLists
dt_list = vDDDLists.from_ical('19960402T010000Z')
dt_list = vDDDLists.from_ical("19960402T010000Z")
self.assertTrue(isinstance(dt_list, list))
self.assertEqual(len(dt_list), 1)
self.assertTrue(isinstance(dt_list[0], datetime))
self.assertEqual(str(dt_list[0]), '1996-04-02 01:00:00+00:00')
self.assertEqual(str(dt_list[0]), "1996-04-02 01:00:00+00:00")
p = '19960402T010000Z,19960403T010000Z,19960404T010000Z'
p = "19960402T010000Z,19960403T010000Z,19960404T010000Z"
dt_list = vDDDLists.from_ical(p)
self.assertEqual(len(dt_list), 3)
self.assertEqual(str(dt_list[0]), '1996-04-02 01:00:00+00:00')
self.assertEqual(str(dt_list[2]), '1996-04-04 01:00:00+00:00')
self.assertEqual(str(dt_list[0]), "1996-04-02 01:00:00+00:00")
self.assertEqual(str(dt_list[2]), "1996-04-04 01:00:00+00:00")
dt_list = vDDDLists([])
self.assertEqual(dt_list.to_ical(), b'')
self.assertEqual(dt_list.to_ical(), b"")
dt_list = vDDDLists([datetime(2000, 1, 1)])
self.assertEqual(dt_list.to_ical(), b'20000101T000000')
self.assertEqual(dt_list.to_ical(), b"20000101T000000")
dt_list = vDDDLists([datetime(2000, 1, 1), datetime(2000, 11, 11)])
self.assertEqual(dt_list.to_ical(), b'20000101T000000,20001111T000000')
self.assertEqual(dt_list.to_ical(), b"20000101T000000,20001111T000000")
instance = vDDDLists([])
self.assertFalse(instance == "value")
def test_prop_vDate(self):
from icalendar.prop import vDate
self.assertEqual(vDate(date(2001, 1, 1)).to_ical(), b'20010101')
self.assertEqual(vDate(date(1899, 1, 1)).to_ical(), b'18990101')
self.assertEqual(vDate(date(2001, 1, 1)).to_ical(), b"20010101")
self.assertEqual(vDate(date(1899, 1, 1)).to_ical(), b"18990101")
self.assertEqual(vDate.from_ical('20010102'), date(2001, 1, 2))
self.assertEqual(vDate.from_ical("20010102"), date(2001, 1, 2))
self.assertRaises(ValueError, vDate, 'd')
self.assertRaises(ValueError, vDate.from_ical, '200102')
self.assertRaises(ValueError, vDate, "d")
self.assertRaises(ValueError, vDate.from_ical, "200102")
def test_prop_vDuration(self):
from icalendar.prop import vDuration
self.assertEqual(vDuration(timedelta(11)).to_ical(), b'P11D')
self.assertEqual(vDuration(timedelta(-14)).to_ical(), b'-P14D')
self.assertEqual(
vDuration(timedelta(1, 7384)).to_ical(),
b'P1DT2H3M4S'
)
self.assertEqual(vDuration(timedelta(1, 7380)).to_ical(), b'P1DT2H3M')
self.assertEqual(vDuration(timedelta(1, 7200)).to_ical(), b'P1DT2H')
self.assertEqual(vDuration(timedelta(0, 7200)).to_ical(), b'PT2H')
self.assertEqual(vDuration(timedelta(0, 7384)).to_ical(), b'PT2H3M4S')
self.assertEqual(vDuration(timedelta(0, 184)).to_ical(), b'PT3M4S')
self.assertEqual(vDuration(timedelta(0, 22)).to_ical(), b'PT22S')
self.assertEqual(vDuration(timedelta(0, 3622)).to_ical(), b'PT1H0M22S')
self.assertEqual(vDuration(timedelta(days=1, hours=5)).to_ical(),
b'P1DT5H')
self.assertEqual(vDuration(timedelta(hours=-5)).to_ical(), b'-PT5H')
self.assertEqual(vDuration(timedelta(days=-1, hours=-5)).to_ical(),
b'-P1DT5H')
self.assertEqual(vDuration(timedelta(11)).to_ical(), b"P11D")
self.assertEqual(vDuration(timedelta(-14)).to_ical(), b"-P14D")
self.assertEqual(vDuration(timedelta(1, 7384)).to_ical(), b"P1DT2H3M4S")
self.assertEqual(vDuration(timedelta(1, 7380)).to_ical(), b"P1DT2H3M")
self.assertEqual(vDuration(timedelta(1, 7200)).to_ical(), b"P1DT2H")
self.assertEqual(vDuration(timedelta(0, 7200)).to_ical(), b"PT2H")
self.assertEqual(vDuration(timedelta(0, 7384)).to_ical(), b"PT2H3M4S")
self.assertEqual(vDuration(timedelta(0, 184)).to_ical(), b"PT3M4S")
self.assertEqual(vDuration(timedelta(0, 22)).to_ical(), b"PT22S")
self.assertEqual(vDuration(timedelta(0, 3622)).to_ical(), b"PT1H0M22S")
self.assertEqual(vDuration(timedelta(days=1, hours=5)).to_ical(), b"P1DT5H")
self.assertEqual(vDuration(timedelta(hours=-5)).to_ical(), b"-PT5H")
self.assertEqual(vDuration(timedelta(days=-1, hours=-5)).to_ical(), b"-P1DT5H")
# How does the parsing work?
self.assertEqual(vDuration.from_ical('PT1H0M22S'), timedelta(0, 3622))
self.assertEqual(vDuration.from_ical("PT1H0M22S"), timedelta(0, 3622))
self.assertRaises(ValueError, vDuration.from_ical, 'kox')
self.assertRaises(ValueError, vDuration.from_ical, "kox")
self.assertEqual(vDuration.from_ical('-P14D'), timedelta(-14))
self.assertEqual(vDuration.from_ical("-P14D"), timedelta(-14))
self.assertRaises(ValueError, vDuration, 11)
# calling to_ical twice should result in same output
duration = vDuration(timedelta(days=-1, hours=-5))
self.assertEqual(duration.to_ical(), b'-P1DT5H')
self.assertEqual(duration.to_ical(), b'-P1DT5H')
self.assertEqual(duration.to_ical(), b"-P1DT5H")
self.assertEqual(duration.to_ical(), b"-P1DT5H")
def test_prop_vWeekday(self):
from icalendar.prop import vWeekday
self.assertEqual(vWeekday('mo').to_ical(), b'MO')
self.assertRaises(ValueError, vWeekday, 'erwer')
self.assertEqual(vWeekday.from_ical('mo'), 'MO')
self.assertEqual(vWeekday.from_ical('+3mo'), '+3MO')
self.assertRaises(ValueError, vWeekday.from_ical, 'Saturday')
self.assertEqual(vWeekday('+mo').to_ical(), b'+MO')
self.assertEqual(vWeekday('+3mo').to_ical(), b'+3MO')
self.assertEqual(vWeekday('-tu').to_ical(), b'-TU')
self.assertEqual(vWeekday("mo").to_ical(), b"MO")
self.assertRaises(ValueError, vWeekday, "erwer")
self.assertEqual(vWeekday.from_ical("mo"), "MO")
self.assertEqual(vWeekday.from_ical("+3mo"), "+3MO")
self.assertRaises(ValueError, vWeekday.from_ical, "Saturday")
self.assertEqual(vWeekday("+mo").to_ical(), b"+MO")
self.assertEqual(vWeekday("+3mo").to_ical(), b"+3MO")
self.assertEqual(vWeekday("-tu").to_ical(), b"-TU")
def test_prop_vFrequency(self):
from icalendar.prop import vFrequency
self.assertRaises(ValueError, vFrequency, 'bad test')
self.assertEqual(vFrequency('daily').to_ical(), b'DAILY')
self.assertEqual(vFrequency('daily').from_ical('MONTHLY'), 'MONTHLY')
self.assertRaises(ValueError, vFrequency, "bad test")
self.assertEqual(vFrequency("daily").to_ical(), b"DAILY")
self.assertEqual(vFrequency("daily").from_ical("MONTHLY"), "MONTHLY")
self.assertRaises(ValueError, vFrequency.from_ical, 234)
def test_prop_vRecur(self):
@ -121,123 +115,117 @@ class TestProp(unittest.TestCase):
# Let's see how close we can get to one from the rfc:
# FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30
r = dict({'freq': 'yearly', 'interval': 2})
r.update({
'bymonth': 1,
'byday': 'su',
'byhour': [8, 9],
'byminute': 30
})
r = dict({"freq": "yearly", "interval": 2})
r.update({"bymonth": 1, "byday": "su", "byhour": [8, 9], "byminute": 30})
self.assertEqual(
vRecur(r).to_ical(),
b'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1'
b"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1",
)
r = vRecur(FREQ='yearly', INTERVAL=2)
r.update({
'BYMONTH': 1,
'BYDAY': 'su',
'BYHOUR': [8, 9],
'BYMINUTE': 30,
})
r = vRecur(FREQ="yearly", INTERVAL=2)
r.update(
{
"BYMONTH": 1,
"BYDAY": "su",
"BYHOUR": [8, 9],
"BYMINUTE": 30,
}
)
self.assertEqual(
r.to_ical(),
b'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1'
b"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1",
)
r = vRecur(freq='DAILY', count=10)
r['bysecond'] = [0, 15, 30, 45]
self.assertEqual(r.to_ical(),
b'FREQ=DAILY;COUNT=10;BYSECOND=0,15,30,45')
r = vRecur(freq="DAILY", count=10)
r["bysecond"] = [0, 15, 30, 45]
self.assertEqual(r.to_ical(), b"FREQ=DAILY;COUNT=10;BYSECOND=0,15,30,45")
r = vRecur(freq='DAILY', until=datetime(2005, 1, 1, 12, 0, 0))
self.assertEqual(r.to_ical(), b'FREQ=DAILY;UNTIL=20050101T120000')
r = vRecur(freq="DAILY", until=datetime(2005, 1, 1, 12, 0, 0))
self.assertEqual(r.to_ical(), b"FREQ=DAILY;UNTIL=20050101T120000")
# How do we fare with regards to parsing?
r = vRecur.from_ical('FREQ=DAILY;INTERVAL=2;COUNT=10')
self.assertEqual(r,
{'COUNT': [10], 'FREQ': ['DAILY'], 'INTERVAL': [2]})
self.assertEqual(
vRecur(r).to_ical(),
b'FREQ=DAILY;COUNT=10;INTERVAL=2'
)
r = vRecur.from_ical("FREQ=DAILY;INTERVAL=2;COUNT=10")
self.assertEqual(r, {"COUNT": [10], "FREQ": ["DAILY"], "INTERVAL": [2]})
self.assertEqual(vRecur(r).to_ical(), b"FREQ=DAILY;COUNT=10;INTERVAL=2")
r = vRecur.from_ical('FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=-SU;'
'BYHOUR=8,9;BYMINUTE=30')
r = vRecur.from_ical(
"FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=-SU;" "BYHOUR=8,9;BYMINUTE=30"
)
self.assertEqual(
r,
{'BYHOUR': [8, 9], 'BYDAY': ['-SU'], 'BYMINUTE': [30],
'BYMONTH': [1], 'FREQ': ['YEARLY'], 'INTERVAL': [2]}
{
"BYHOUR": [8, 9],
"BYDAY": ["-SU"],
"BYMINUTE": [30],
"BYMONTH": [1],
"FREQ": ["YEARLY"],
"INTERVAL": [2],
},
)
self.assertEqual(
vRecur(r).to_ical(),
b'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=-SU;'
b'BYMONTH=1'
b"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=-SU;" b"BYMONTH=1",
)
r = vRecur.from_ical('FREQ=WEEKLY;INTERVAL=1;BYWEEKDAY=TH')
r = vRecur.from_ical("FREQ=WEEKLY;INTERVAL=1;BYWEEKDAY=TH")
self.assertEqual(
r,
{'FREQ': ['WEEKLY'], 'INTERVAL': [1], 'BYWEEKDAY': ['TH']}
)
self.assertEqual(r, {"FREQ": ["WEEKLY"], "INTERVAL": [1], "BYWEEKDAY": ["TH"]})
self.assertEqual(
vRecur(r).to_ical(),
b'FREQ=WEEKLY;INTERVAL=1;BYWEEKDAY=TH'
)
self.assertEqual(vRecur(r).to_ical(), b"FREQ=WEEKLY;INTERVAL=1;BYWEEKDAY=TH")
# Some examples from the spec
r = vRecur.from_ical('FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1')
self.assertEqual(vRecur(r).to_ical(),
b'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1')
r = vRecur.from_ical("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1")
self.assertEqual(
vRecur(r).to_ical(), b"FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1"
)
p = 'FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30'
p = "FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30"
r = vRecur.from_ical(p)
self.assertEqual(
vRecur(r).to_ical(),
b'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1'
b"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1",
)
# and some errors
self.assertRaises(ValueError, vRecur.from_ical, 'BYDAY=12')
self.assertRaises(ValueError, vRecur.from_ical, "BYDAY=12")
# when key is not RFC-compliant, parse it as vText
r = vRecur.from_ical('FREQ=MONTHLY;BYOTHER=TEXT;BYEASTER=-3')
self.assertEqual(vRecur(r).to_ical(),
b'FREQ=MONTHLY;BYEASTER=-3;BYOTHER=TEXT')
r = vRecur.from_ical("FREQ=MONTHLY;BYOTHER=TEXT;BYEASTER=-3")
self.assertEqual(vRecur(r).to_ical(), b"FREQ=MONTHLY;BYEASTER=-3;BYOTHER=TEXT")
def test_prop_vText(self):
from icalendar.prop import vText
self.assertEqual(vText('Simple text').to_ical(), b'Simple text')
self.assertEqual(vText("Simple text").to_ical(), b"Simple text")
# Escaped text
t = vText('Text ; with escaped, chars')
self.assertEqual(t.to_ical(), b'Text \\; with escaped\\, chars')
t = vText("Text ; with escaped, chars")
self.assertEqual(t.to_ical(), b"Text \\; with escaped\\, chars")
# Escaped newlines
self.assertEqual(vText('Text with escaped\\N chars').to_ical(),
b'Text with escaped\\n chars')
self.assertEqual(
vText("Text with escaped\\N chars").to_ical(), b"Text with escaped\\n chars"
)
# If you pass a unicode object, it will be utf-8 encoded. As this is
# the (only) standard that RFC 5545 support.
t = vText('international chars \xe4\xf6\xfc')
self.assertEqual(t.to_ical(),
b'international chars \xc3\xa4\xc3\xb6\xc3\xbc')
t = vText("international chars \xe4\xf6\xfc")
self.assertEqual(t.to_ical(), b"international chars \xc3\xa4\xc3\xb6\xc3\xbc")
# and parsing?
self.assertEqual(vText.from_ical('Text \\; with escaped\\, chars'),
'Text ; with escaped, chars')
self.assertEqual(
vText.from_ical("Text \\; with escaped\\, chars"),
"Text ; with escaped, chars",
)
t = vText.from_ical('A string with\\; some\\\\ characters in\\it')
t = vText.from_ical("A string with\\; some\\\\ characters in\\it")
self.assertEqual(t, "A string with; some\\ characters in\\it")
# We are forgiving to utf-8 encoding errors:
# We intentionally use a string with unexpected encoding
#
self.assertEqual(vText.from_ical(b'Ol\xe9'), 'Ol\ufffd')
self.assertEqual(vText.from_ical(b"Ol\xe9"), "Ol\ufffd")
# Notice how accented E character, encoded with latin-1, got replaced
# with the official U+FFFD REPLACEMENT CHARACTER.
@ -245,100 +233,96 @@ class TestProp(unittest.TestCase):
def test_prop_vTime(self):
from icalendar.prop import vTime
self.assertEqual(vTime(12, 30, 0).to_ical(), '123000')
self.assertEqual(vTime.from_ical('123000'), time(12, 30))
self.assertEqual(vTime(12, 30, 0).to_ical(), "123000")
self.assertEqual(vTime.from_ical("123000"), time(12, 30))
# We should also fail, right?
self.assertRaises(ValueError, vTime.from_ical, '263000')
self.assertRaises(ValueError, vTime.from_ical, "263000")
self.assertRaises(ValueError, vTime, '263000')
self.assertRaises(ValueError, vTime, "263000")
def test_prop_vUri(self):
from icalendar.prop import vUri
self.assertEqual(vUri('http://www.example.com/').to_ical(),
b'http://www.example.com/')
self.assertEqual(vUri.from_ical('http://www.example.com/'),
'http://www.example.com/')
self.assertEqual(
vUri("http://www.example.com/").to_ical(), b"http://www.example.com/"
)
self.assertEqual(
vUri.from_ical("http://www.example.com/"), "http://www.example.com/"
)
def test_prop_vGeo(self):
from icalendar.prop import vGeo
# Pass a list
self.assertEqual(vGeo([1.2, 3.0]).to_ical(), '1.2;3.0')
self.assertEqual(vGeo([1.2, 3.0]).to_ical(), "1.2;3.0")
# Pass a tuple
self.assertEqual(vGeo((1.2, 3.0)).to_ical(), '1.2;3.0')
self.assertEqual(vGeo((1.2, 3.0)).to_ical(), "1.2;3.0")
g = vGeo.from_ical('37.386013;-122.082932')
self.assertEqual(g, (float('37.386013'), float('-122.082932')))
g = vGeo.from_ical("37.386013;-122.082932")
self.assertEqual(g, (float("37.386013"), float("-122.082932")))
self.assertEqual(vGeo(g).to_ical(), '37.386013;-122.082932')
self.assertEqual(vGeo(g).to_ical(), "37.386013;-122.082932")
self.assertRaises(ValueError, vGeo, 'g')
self.assertRaises(ValueError, vGeo.from_ical, '1s3;1s3')
self.assertRaises(ValueError, vGeo, "g")
self.assertRaises(ValueError, vGeo.from_ical, "1s3;1s3")
def test_prop_vUTCOffset(self):
from icalendar.prop import vUTCOffset
self.assertEqual(vUTCOffset(timedelta(hours=2)).to_ical(), '+0200')
self.assertEqual(vUTCOffset(timedelta(hours=2)).to_ical(), "+0200")
self.assertEqual(vUTCOffset(timedelta(hours=-5)).to_ical(), '-0500')
self.assertEqual(vUTCOffset(timedelta(hours=-5)).to_ical(), "-0500")
self.assertEqual(vUTCOffset(timedelta()).to_ical(), '+0000')
self.assertEqual(vUTCOffset(timedelta()).to_ical(), "+0000")
self.assertEqual(vUTCOffset(timedelta(minutes=-30)).to_ical(),
'-0030')
self.assertEqual(vUTCOffset(timedelta(minutes=-30)).to_ical(), "-0030")
self.assertEqual(
vUTCOffset(timedelta(hours=2, minutes=-30)).to_ical(),
'+0130'
)
self.assertEqual(vUTCOffset(timedelta(hours=2, minutes=-30)).to_ical(), "+0130")
self.assertEqual(vUTCOffset(timedelta(hours=1, minutes=30)).to_ical(),
'+0130')
self.assertEqual(vUTCOffset(timedelta(hours=1, minutes=30)).to_ical(), "+0130")
# Support seconds
self.assertEqual(vUTCOffset(timedelta(hours=1,
minutes=30,
seconds=7)).to_ical(), '+013007')
self.assertEqual(
vUTCOffset(timedelta(hours=1, minutes=30, seconds=7)).to_ical(), "+013007"
)
# Parsing
self.assertEqual(vUTCOffset.from_ical('0000'), timedelta(0))
self.assertEqual(vUTCOffset.from_ical('-0030'), timedelta(-1, 84600))
self.assertEqual(vUTCOffset.from_ical('+0200'), timedelta(0, 7200))
self.assertEqual(vUTCOffset.from_ical('+023040'), timedelta(0, 9040))
self.assertEqual(vUTCOffset.from_ical("0000"), timedelta(0))
self.assertEqual(vUTCOffset.from_ical("-0030"), timedelta(-1, 84600))
self.assertEqual(vUTCOffset.from_ical("+0200"), timedelta(0, 7200))
self.assertEqual(vUTCOffset.from_ical("+023040"), timedelta(0, 9040))
self.assertEqual(vUTCOffset(vUTCOffset.from_ical('+0230')).to_ical(),
'+0230')
self.assertEqual(vUTCOffset(vUTCOffset.from_ical("+0230")).to_ical(), "+0230")
# And a few failures
self.assertRaises(ValueError, vUTCOffset.from_ical, '+323k')
self.assertRaises(ValueError, vUTCOffset.from_ical, "+323k")
self.assertRaises(ValueError, vUTCOffset.from_ical, '+2400')
self.assertRaises(ValueError, vUTCOffset.from_ical, "+2400")
self.assertRaises(ValueError, vUTCOffset, '0:00:00')
self.assertRaises(ValueError, vUTCOffset, "0:00:00")
def test_prop_vInline(self):
from icalendar.prop import vInline
self.assertEqual(vInline('Some text'), 'Some text')
self.assertEqual(vInline('Some text').to_ical(), b'Some text')
self.assertEqual(vInline.from_ical('Some text'), 'Some text')
self.assertEqual(vInline("Some text"), "Some text")
self.assertEqual(vInline("Some text").to_ical(), b"Some text")
self.assertEqual(vInline.from_ical("Some text"), "Some text")
t2 = vInline('other text')
t2.params['cn'] = 'Test Osterone'
t2 = vInline("other text")
t2.params["cn"] = "Test Osterone"
self.assertIsInstance(t2.params, Parameters)
self.assertEqual(t2.params, {'CN': 'Test Osterone'})
self.assertEqual(t2.params, {"CN": "Test Osterone"})
def test_prop_vCategory(self):
from icalendar.prop import vCategory
catz = ['cat 1', 'cat 2', 'cat 3']
catz = ["cat 1", "cat 2", "cat 3"]
v_cat = vCategory(catz)
self.assertEqual(v_cat.to_ical(), b'cat 1,cat 2,cat 3')
self.assertEqual(v_cat.to_ical(), b"cat 1,cat 2,cat 3")
self.assertEqual(vCategory.from_ical(v_cat.to_ical()), catz)
c = vCategory(vCategory.from_ical("APPOINTMENT,EDUCATION"))
cats = list(c)
@ -349,27 +333,31 @@ class TestProp(unittest.TestCase):
# To get a type you can use it like this.
factory = TypesFactory()
datetime_parser = factory['date-time']
self.assertEqual(datetime_parser(datetime(2001, 1, 1)).to_ical(),
b'20010101T000000')
datetime_parser = factory["date-time"]
self.assertEqual(
datetime_parser(datetime(2001, 1, 1)).to_ical(), b"20010101T000000"
)
# A typical use is when the parser tries to find a content type and use
# text as the default
value = '20050101T123000'
value_type = 'date-time'
self.assertEqual(factory.get(value_type, 'text').from_ical(value),
datetime(2005, 1, 1, 12, 30))
value = "20050101T123000"
value_type = "date-time"
self.assertEqual(
factory.get(value_type, "text").from_ical(value),
datetime(2005, 1, 1, 12, 30),
)
# It can also be used to directly encode property and parameter values
self.assertEqual(
factory.to_ical('comment', 'by Rasmussen, Max M\xfcller'),
b'by Rasmussen\\, Max M\xc3\xbcller'
factory.to_ical("comment", "by Rasmussen, Max M\xfcller"),
b"by Rasmussen\\, Max M\xc3\xbcller",
)
self.assertEqual(factory.to_ical('priority', 1), b'1')
self.assertEqual(factory.to_ical('cn', 'Rasmussen, Max M\xfcller'),
b'Rasmussen\\, Max M\xc3\xbcller')
self.assertEqual(factory.to_ical("priority", 1), b"1")
self.assertEqual(
factory.from_ical('cn', b'Rasmussen\\, Max M\xc3\xb8ller'),
'Rasmussen, Max M\xf8ller'
factory.to_ical("cn", "Rasmussen, Max M\xfcller"),
b"Rasmussen\\, Max M\xc3\xbcller",
)
self.assertEqual(
factory.from_ical("cn", b"Rasmussen\\, Max M\xc3\xb8ller"),
"Rasmussen, Max M\xf8ller",
)

Wyświetl plik

@ -1,4 +1,5 @@
"""Test vBinary"""
import pytest
from icalendar import vBinary
@ -6,36 +7,39 @@ from icalendar.parser import Parameters
def test_text():
txt = b'This is gibberish'
txt_ical = b'VGhpcyBpcyBnaWJiZXJpc2g='
assert (vBinary(txt).to_ical() == txt_ical)
assert (vBinary.from_ical(txt_ical) == txt)
txt = b"This is gibberish"
txt_ical = b"VGhpcyBpcyBnaWJiZXJpc2g="
assert vBinary(txt).to_ical() == txt_ical
assert vBinary.from_ical(txt_ical) == txt
def test_binary():
txt = b'Binary data \x13 \x56'
txt_ical = b'QmluYXJ5IGRhdGEgEyBW'
assert (vBinary(txt).to_ical() == txt_ical)
assert (vBinary.from_ical(txt_ical) == txt)
txt = b"Binary data \x13 \x56"
txt_ical = b"QmluYXJ5IGRhdGEgEyBW"
assert vBinary(txt).to_ical() == txt_ical
assert vBinary.from_ical(txt_ical) == txt
def test_param():
assert isinstance(vBinary('txt').params, Parameters)
assert (
vBinary('txt').params == {'VALUE': 'BINARY', 'ENCODING': 'BASE64'}
)
assert isinstance(vBinary("txt").params, Parameters)
assert vBinary("txt").params == {"VALUE": "BINARY", "ENCODING": "BASE64"}
def test_long_data():
"""Long data should not have line breaks, as that would interfere"""
txt = b'a' * 99
txt_ical = b'YWFh' * 33
assert (vBinary(txt).to_ical() == txt_ical)
assert (vBinary.from_ical(txt_ical) == txt)
txt = b"a" * 99
txt_ical = b"YWFh" * 33
assert vBinary(txt).to_ical() == txt_ical
assert vBinary.from_ical(txt_ical) == txt
def test_repr():
instance = vBinary("value")
assert repr(instance) == "vBinary(b'dmFsdWU=')"
def test_from_ical():
with pytest.raises(ValueError, match='Not valid base 64 encoding.'):
with pytest.raises(ValueError, match="Not valid base 64 encoding."):
vBinary.from_ical("value")
with pytest.raises(ValueError, match='Not valid base 64 encoding.'):
vBinary.from_ical("áèਮ")
with pytest.raises(ValueError, match="Not valid base 64 encoding."):
vBinary.from_ical("áèਮ")

Wyświetl plik

@ -1,17 +1,22 @@
from icalendar.prop import vBoolean
import pytest
from icalendar.prop import vBoolean
def test_true():
assert (vBoolean(True).to_ical() == b'TRUE')
assert vBoolean(True).to_ical() == b"TRUE"
def test_false():
assert (vBoolean(0).to_ical() == b'FALSE')
assert vBoolean(0).to_ical() == b"FALSE"
def test_roundtrip():
assert (vBoolean.from_ical(vBoolean(True).to_ical()) == True)
assert (vBoolean.from_ical('true') == True)
assert vBoolean.from_ical(vBoolean(True).to_ical()) == True
assert vBoolean.from_ical("true") == True
def test_error():
"""Error: key not exists"""
with pytest.raises(ValueError):
vBoolean.from_ical('ture')
vBoolean.from_ical("ture")

Wyświetl plik

@ -1,9 +1,9 @@
from icalendar.prop import vCalAddress
from icalendar.parser import Parameters
from icalendar.prop import vCalAddress
txt = b'MAILTO:maxm@mxm.dk'
txt = b"MAILTO:maxm@mxm.dk"
a = vCalAddress(txt)
a.params['cn'] = 'Max M'
a.params["cn"] = "Max M"
def test_to_ical():
@ -11,14 +11,14 @@ def test_to_ical():
def test_params():
assert isinstance(a.params, Parameters)
assert a.params == {'CN': 'Max M'}
assert isinstance(a.params, Parameters)
assert a.params == {"CN": "Max M"}
def test_from_ical():
assert vCalAddress.from_ical(txt) == 'MAILTO:maxm@mxm.dk'
assert vCalAddress.from_ical(txt) == "MAILTO:maxm@mxm.dk"
def test_repr():
instance = vCalAddress("value")
assert repr(instance) == "vCalAddress('value')"
assert repr(instance) == "vCalAddress('value')"

Wyświetl plik

@ -1,30 +1,35 @@
from icalendar.prop import vDDDTypes
from datetime import date, datetime, timedelta, time
from datetime import date, datetime, time, timedelta
import pytest
from icalendar.prop import vDDDTypes
def test_instance():
assert isinstance(vDDDTypes.from_ical('20010101T123000'), datetime)
assert isinstance(vDDDTypes.from_ical('20010101'), date)
assert isinstance(vDDDTypes.from_ical("20010101T123000"), datetime)
assert isinstance(vDDDTypes.from_ical("20010101"), date)
def test_datetime_with_timezone(tzp):
assert vDDDTypes.from_ical('20010101T123000Z') == \
tzp.localize_utc(datetime(2001, 1, 1, 12, 30))
assert vDDDTypes.from_ical("20010101T123000Z") == tzp.localize_utc(
datetime(2001, 1, 1, 12, 30)
)
def test_timedelta():
assert vDDDTypes.from_ical('P31D') == timedelta(31)
assert vDDDTypes.from_ical('-P31D') == timedelta(-31)
assert vDDDTypes.from_ical("P31D") == timedelta(31)
assert vDDDTypes.from_ical("-P31D") == timedelta(-31)
def test_bad_input():
with pytest.raises(ValueError):
vDDDTypes(42)
def test_time_from_string():
assert vDDDTypes.from_ical('123000') == time(12, 30)
assert isinstance(vDDDTypes.from_ical('123000'), time)
assert vDDDTypes.from_ical("123000") == time(12, 30)
assert isinstance(vDDDTypes.from_ical("123000"), time)
def test_invalid_period_to_ical():
invalid_period = (datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 2))

Wyświetl plik

@ -1,42 +1,47 @@
from icalendar.prop import vDatetime
import pytest
from datetime import datetime
import pytest
from icalendar.prop import vDatetime
def test_to_ical():
assert vDatetime(datetime(2001, 1, 1, 12, 30, 0)).to_ical() == b'20010101T123000'
assert vDatetime(datetime(2001, 1, 1, 12, 30, 0)).to_ical() == b"20010101T123000"
def test_from_ical():
assert vDatetime.from_ical('20000101T120000') == datetime(2000, 1, 1, 12, 0)
assert vDatetime.from_ical('20010101T000000') == datetime(2001, 1, 1, 0, 0)
assert vDatetime.from_ical("20000101T120000") == datetime(2000, 1, 1, 12, 0)
assert vDatetime.from_ical("20010101T000000") == datetime(2001, 1, 1, 0, 0)
def test_to_ical_utc(tzp):
dutc = tzp.localize_utc(datetime(2001, 1, 1, 12, 30, 0))
assert vDatetime(dutc).to_ical() == b'20010101T123000Z'
assert vDatetime(dutc).to_ical() == b"20010101T123000Z"
def test_to_ical_utc_1899(tzp):
dutc = tzp.localize_utc(datetime(1899, 1, 1, 12, 30, 0))
assert vDatetime(dutc).to_ical() == b'18990101T123000Z'
assert vDatetime(dutc).to_ical() == b"18990101T123000Z"
def test_bad_ical():
with pytest.raises(ValueError):
vDatetime.from_ical('20010101T000000A')
vDatetime.from_ical("20010101T000000A")
def test_roundtrip():
utc = vDatetime.from_ical('20010101T000000Z')
assert vDatetime(utc).to_ical() == b'20010101T000000Z'
utc = vDatetime.from_ical("20010101T000000Z")
assert vDatetime(utc).to_ical() == b"20010101T000000Z"
def test_transition(tzp):
# 1 minute before transition to DST
dat = vDatetime.from_ical('20120311T015959', 'America/Denver')
assert dat.strftime('%Y%m%d%H%M%S %z') =='20120311015959 -0700'
dat = vDatetime.from_ical("20120311T015959", "America/Denver")
assert dat.strftime("%Y%m%d%H%M%S %z") == "20120311015959 -0700"
# After transition to DST
dat = vDatetime.from_ical('20120311T030000', 'America/Denver')
assert dat.strftime('%Y%m%d%H%M%S %z') == '20120311030000 -0600'
dat = vDatetime.from_ical("20120311T030000", "America/Denver")
assert dat.strftime("%Y%m%d%H%M%S %z") == "20120311030000 -0600"
dat = vDatetime.from_ical('20101010T000000', 'Europe/Vienna')
assert vDatetime(dat).to_ical() == b'20101010T000000'
dat = vDatetime.from_ical("20101010T000000", "Europe/Vienna")
assert vDatetime(dat).to_ical() == b"20101010T000000"

Wyświetl plik

@ -1,63 +1,61 @@
import unittest
from icalendar.prop import vPeriod
from datetime import datetime, timedelta
import pytest
from icalendar.prop import vPeriod
class TestProp(unittest.TestCase):
def test_one_day(self):
# One day in exact datetimes
per = (datetime(2000, 1, 1), datetime(2000, 1, 2))
self.assertEqual(vPeriod(per).to_ical(),
b'20000101T000000/20000102T000000')
self.assertEqual(vPeriod(per).to_ical(), b"20000101T000000/20000102T000000")
per = (datetime(2000, 1, 1), timedelta(days=31))
self.assertEqual(vPeriod(per).to_ical(), b'20000101T000000/P31D')
self.assertEqual(vPeriod(per).to_ical(), b"20000101T000000/P31D")
def test_roundtrip(self):
p = vPeriod.from_ical('20000101T000000/20000102T000000')
self.assertEqual(
p,
(datetime(2000, 1, 1, 0, 0), datetime(2000, 1, 2, 0, 0))
)
self.assertEqual(vPeriod(p).to_ical(),
b'20000101T000000/20000102T000000')
p = vPeriod.from_ical("20000101T000000/20000102T000000")
self.assertEqual(p, (datetime(2000, 1, 1, 0, 0), datetime(2000, 1, 2, 0, 0)))
self.assertEqual(vPeriod(p).to_ical(), b"20000101T000000/20000102T000000")
self.assertEqual(vPeriod.from_ical('20000101T000000/P31D'),
(datetime(2000, 1, 1, 0, 0), timedelta(31)))
self.assertEqual(
vPeriod.from_ical("20000101T000000/P31D"),
(datetime(2000, 1, 1, 0, 0), timedelta(31)),
)
def test_round_trip_with_absolute_time(self):
p = vPeriod.from_ical('20000101T000000Z/20000102T000000Z')
self.assertEqual(vPeriod(p).to_ical(),
b'20000101T000000Z/20000102T000000Z')
p = vPeriod.from_ical("20000101T000000Z/20000102T000000Z")
self.assertEqual(vPeriod(p).to_ical(), b"20000101T000000Z/20000102T000000Z")
def test_bad_input(self):
self.assertRaises(ValueError,
vPeriod.from_ical, '20000101T000000/Psd31D')
self.assertRaises(ValueError, vPeriod.from_ical, "20000101T000000/Psd31D")
def test_timezoned(tzp):
start = tzp.localize(datetime(2000, 1, 1), 'Europe/Copenhagen')
end = tzp.localize(datetime(2000, 1, 2), 'Europe/Copenhagen')
start = tzp.localize(datetime(2000, 1, 1), "Europe/Copenhagen")
end = tzp.localize(datetime(2000, 1, 2), "Europe/Copenhagen")
per = (start, end)
assert vPeriod(per).to_ical() == b'20000101T000000/20000102T000000'
assert vPeriod(per).params['TZID'] == 'Europe/Copenhagen'
assert vPeriod(per).to_ical() == b"20000101T000000/20000102T000000"
assert vPeriod(per).params["TZID"] == "Europe/Copenhagen"
def test_timezoned_with_timedelta(tzp):
p = vPeriod((tzp.localize(datetime(2000, 1, 1), 'Europe/Copenhagen'), timedelta(days=31)))
assert p.to_ical() == b'20000101T000000/P31D'
p = vPeriod(
(tzp.localize(datetime(2000, 1, 1), "Europe/Copenhagen"), timedelta(days=31))
)
assert p.to_ical() == b"20000101T000000/P31D"
@pytest.mark.parametrize(
"params",
[
('20000101T000000', datetime(2000, 1, 2)),
(datetime(2000, 1, 1), '20000102T000000'),
("20000101T000000", datetime(2000, 1, 2)),
(datetime(2000, 1, 1), "20000102T000000"),
(datetime(2000, 1, 2), datetime(2000, 1, 1)),
(datetime(2000, 1, 2), timedelta(-1)),
]
],
)
def test_invalid_parameters(params):
"""The parameters are of wrong type or of wrong order."""

Wyświetl plik

@ -1,14 +1,17 @@
"""Test the mappings from windows to olson tzids"""
from icalendar.timezone.windows_to_olson import WINDOWS_TO_OLSON
import pytest
from icalendar import vDatetime
from datetime import datetime
import pytest
from icalendar import vDatetime
from icalendar.timezone.windows_to_olson import WINDOWS_TO_OLSON
def test_windows_timezone(tzp):
"""Test that the timezone is mapped correctly to olson."""
dt = vDatetime.from_ical('20170507T181920', 'Eastern Standard Time')
expected = tzp.localize(datetime(2017, 5, 7, 18, 19, 20), 'America/New_York')
dt = vDatetime.from_ical("20170507T181920", "Eastern Standard Time")
expected = tzp.localize(datetime(2017, 5, 7, 18, 19, 20), "America/New_York")
assert dt.tzinfo == dt.tzinfo
assert dt == expected

Wyświetl plik

@ -1,2 +1,4 @@
def test_bom_calendar(calendars):
assert calendars.bom_calendar.walk('VCALENDAR'), "Unable to parse a calendar starting with an Unicode BOM"
assert calendars.bom_calendar.walk(
"VCALENDAR"
), "Unable to parse a calendar starting with an Unicode BOM"

Wyświetl plik

@ -1,13 +1,14 @@
import unittest
from datetime import datetime
from icalendar import Calendar, cli
try:
import zoneinfo
except ModuleNotFoundError:
from backports import zoneinfo
INPUT = '''
INPUT = """
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
@ -39,16 +40,23 @@ DTSTART:20220511
DURATION:P5D
END:VEVENT
END:VCALENDAR
'''
"""
def local_datetime(dt):
return datetime.strptime(dt, "%Y%m%dT%H%M%S").replace(tzinfo=zoneinfo.ZoneInfo("Europe/Warsaw")).astimezone().strftime('%c')
return (
datetime.strptime(dt, "%Y%m%dT%H%M%S")
.replace(tzinfo=zoneinfo.ZoneInfo("Europe/Warsaw"))
.astimezone()
.strftime("%c")
)
# datetimes are displayed in the local timezone, so we cannot just hardcode them
firststart = local_datetime('20220820T103400')
firstend = local_datetime('20220820T113400')
secondstart = local_datetime('20220820T200000')
secondend = local_datetime('20220820T203000')
firststart = local_datetime("20220820T103400")
firstend = local_datetime("20220820T113400")
secondstart = local_datetime("20220820T200000")
secondend = local_datetime("20220820T203000")
PROPER_OUTPUT = f""" Organizer: organizer <organizer@test.test>
Attendees:
@ -91,15 +99,16 @@ PROPER_OUTPUT = f""" Organizer: organizer <organizer@test.test>
"""
class CLIToolTest(unittest.TestCase):
def test_output_is_proper(self):
self.maxDiff = None
calendar = Calendar.from_ical(INPUT)
output = ''
for event in calendar.walk('vevent'):
output += cli.view(event) + '\n\n'
output = ""
for event in calendar.walk("vevent"):
output += cli.view(event) + "\n\n"
self.assertEqual(PROPER_OUTPUT, output)
if __name__ == '__main__':
unittest.main()
if __name__ == "__main__":
unittest.main()

Wyświetl plik

@ -2,36 +2,47 @@ import pytest
def test_ignore_exceptions_on_broken_events_issue_104(events):
''' Issue #104 - line parsing error in a VEVENT
"""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.errors == [(None, "Content line could not be parsed into parts: 'X': Invalid content line")]
"""
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
"""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
def test_rdate_dosent_become_none_on_invalid_input_issue_464(events):
'''Issue #464 - [BUG] RDATE can become None if value is invalid
https://github.com/collective/icalendar/issues/464
'''
assert ('RDATE', 'Expected period format, got: 199709T180000Z/PT5H30M') in events.issue_464_invalid_rdate.errors
assert b'RDATE:None' not in events.issue_464_invalid_rdate.to_ical()
@pytest.mark.parametrize('calendar_name', [
'big_bad_calendar',
'small_bad_calendar',
'multiple_calendar_components',
'pr_480_summary_with_colon',
])
def test_rdate_dosent_become_none_on_invalid_input_issue_464(events):
"""Issue #464 - [BUG] RDATE can become None if value is invalid
https://github.com/collective/icalendar/issues/464
"""
assert (
"RDATE",
"Expected period format, got: 199709T180000Z/PT5H30M",
) in events.issue_464_invalid_rdate.errors
assert b"RDATE:None" not in events.issue_464_invalid_rdate.to_ical()
@pytest.mark.parametrize(
"calendar_name",
[
"big_bad_calendar",
"small_bad_calendar",
"multiple_calendar_components",
"pr_480_summary_with_colon",
],
)
def test_error_message_doesnt_get_too_big(calendars, calendar_name):
with pytest.raises(ValueError) as exception:
calendars[calendar_name]
# Ignore part before first : for the test.
assert len(str(exception).split(': ', 1)[1]) <= 100
assert len(str(exception).split(": ", 1)[1]) <= 100

Wyświetl plik

@ -1,71 +1,98 @@
import pytest
import datetime
@pytest.mark.parametrize('field, expected_value', [
('PRODID', '-//Plönë.org//NONSGML plone.app.event//EN'),
('X-WR-CALDESC', 'test non ascii: äöü ÄÖÜ €'),
])
import pytest
@pytest.mark.parametrize(
("field", "expected_value"),
[
("PRODID", "-//Plönë.org//NONSGML plone.app.event//EN"),
("X-WR-CALDESC", "test non ascii: äöü ÄÖÜ €"),
],
)
def test_calendar_from_ical_respects_unicode(field, expected_value, calendars):
cal = calendars.calendar_with_unicode
assert cal[field].to_ical().decode('utf-8') == expected_value
assert cal[field].to_ical().decode("utf-8") == expected_value
@pytest.mark.parametrize('test_input, field, expected_value', [
('event_with_unicode_fields', 'SUMMARY', 'Non-ASCII Test: ÄÖÜ äöü €'),
('event_with_unicode_fields', 'DESCRIPTION', 'icalendar should be able to handle non-ascii: €äüöÄÜÖ.'),
('event_with_unicode_fields', 'LOCATION', 'Tribstrül'),
# Non-unicode characters in summary
# https://github.com/collective/icalendar/issues/64
('issue_64_event_with_non_ascii_summary', 'SUMMARY', 'åäö'),
# Unicode characters in summary
('issue_64_event_with_ascii_summary', 'SUMMARY', 'abcdef'),
])
@pytest.mark.parametrize(
("test_input", "field", "expected_value"),
[
("event_with_unicode_fields", "SUMMARY", "Non-ASCII Test: ÄÖÜ äöü €"),
(
"event_with_unicode_fields",
"DESCRIPTION",
"icalendar should be able to handle non-ascii: €äüöÄÜÖ.",
),
("event_with_unicode_fields", "LOCATION", "Tribstrül"),
# Non-unicode characters in summary
# https://github.com/collective/icalendar/issues/64
("issue_64_event_with_non_ascii_summary", "SUMMARY", "åäö"),
# Unicode characters in summary
("issue_64_event_with_ascii_summary", "SUMMARY", "abcdef"),
],
)
def test_event_from_ical_respects_unicode(test_input, field, expected_value, events):
event = events[test_input]
assert event[field].to_ical().decode('utf-8') == expected_value
assert event[field].to_ical().decode("utf-8") == expected_value
@pytest.mark.parametrize('test_input, expected_output', [
# chokes on umlauts in ORGANIZER
# https://github.com/collective/icalendar/issues/101
('issue_101_icalendar_chokes_on_umlauts_in_organizer', 'acme, ädmin'),
('event_with_unicode_organizer', 'Джон Доу'),
])
@pytest.mark.parametrize(
("test_input", "expected_output"),
[
# chokes on umlauts in ORGANIZER
# https://github.com/collective/icalendar/issues/101
("issue_101_icalendar_chokes_on_umlauts_in_organizer", "acme, ädmin"),
("event_with_unicode_organizer", "Джон Доу"),
],
)
def test_events_parameter_unicoded(events, test_input, expected_output):
assert events[test_input]['ORGANIZER'].params['CN'] == expected_output
assert events[test_input]["ORGANIZER"].params["CN"] == expected_output
def test_parses_event_with_non_ascii_tzid_issue_237(calendars, in_timezone):
"""Issue #237 - Fail to parse timezone with non-ascii TZID
see https://github.com/collective/icalendar/issues/237
"""
start = calendars.issue_237_fail_to_parse_timezone_with_non_ascii_tzid.walk('VEVENT')[0].decoded('DTSTART')
expected = in_timezone(datetime.datetime(2017, 5, 11, 13, 30), 'America/Sao_Paulo')
start = calendars.issue_237_fail_to_parse_timezone_with_non_ascii_tzid.walk(
"VEVENT"
)[0].decoded("DTSTART")
expected = in_timezone(datetime.datetime(2017, 5, 11, 13, 30), "America/Sao_Paulo")
assert not calendars.issue_237_fail_to_parse_timezone_with_non_ascii_tzid.errors
assert start == expected
def test_parses_timezone_with_non_ascii_tzid_issue_237(timezones):
"""Issue #237 - Fail to parse timezone with non-ascii TZID
see https://github.com/collective/icalendar/issues/237
"""
assert timezones.issue_237_brazilia_standard['tzid'] == '(UTC-03:00) Brasília'
assert timezones.issue_237_brazilia_standard["tzid"] == "(UTC-03:00) Brasília"
@pytest.mark.parametrize('timezone_name', ['standard', 'daylight'])
@pytest.mark.parametrize("timezone_name", ["standard", "daylight"])
def test_parses_timezone_with_non_ascii_tzname_issue_273(timezones, timezone_name):
"""Issue #237 - Fail to parse timezone with non-ascii TZID
see https://github.com/collective/icalendar/issues/237
"""
assert timezones.issue_237_brazilia_standard.walk(timezone_name)[0]['TZNAME'] == f'Brasília {timezone_name}'
assert (
timezones.issue_237_brazilia_standard.walk(timezone_name)[0]["TZNAME"]
== f"Brasília {timezone_name}"
)
def test_broken_property(calendars):
"""
Test if error messages are encoded properly.
"""
for event in calendars.broken_ical.walk('vevent'):
assert len(event.errors) == 1, 'Not the right amount of errors.'
for event in calendars.broken_ical.walk("vevent"):
assert len(event.errors) == 1, "Not the right amount of errors."
error = event.errors[0][1]
assert error.startswith('Content line could not be parsed into parts')
assert error.startswith("Content line could not be parsed into parts")
def test_apple_xlocation(calendars):
"""
Test if we support base64 encoded binary data in parameter values.
"""
for event in calendars.x_location.walk('vevent'):
assert len(event.errors) == 0, 'Got too many errors'
for event in calendars.x_location.walk("vevent"):
assert len(event.errors) == 0, "Got too many errors"

Wyświetl plik

@ -1,22 +1,42 @@
"""Test the equality and inequality of components."""
import contextlib
import copy
try:
import pytz
from pytz import UnknownTimeZoneError
except ImportError:
pytz = None
from icalendar.prop import *
from datetime import datetime, date, time, timedelta
class UnknownTimeZoneError(Exception):
pass
from datetime import date, datetime, time, timedelta
import pytest
from icalendar.prop import (
vBinary,
vBoolean,
vCategory,
vDate,
vDatetime,
vDDDLists,
vDDDTypes,
vDuration,
vGeo,
vPeriod,
vText,
vTime,
)
def assert_equal(actual_value, expected_value):
"""Make sure both values are equal"""
assert actual_value == expected_value
assert not actual_value != expected_value
assert actual_value == expected_value
def assert_not_equal(actual_value, expected_value):
"""Make sure both values are not equal"""
assert not actual_value == expected_value
assert actual_value != expected_value
assert actual_value != expected_value
@ -42,8 +62,10 @@ def test_parsed_calendars_are_equal_if_from_same_source(ics_file, tzp):
def test_copies_are_equal(ics_file, tzp):
"""Ensure that copies are equal."""
copy1 = ics_file.copy(); copy1.subcomponents = ics_file.subcomponents
copy2 = ics_file.copy(); copy2.subcomponents = ics_file.subcomponents[:]
copy1 = ics_file.copy()
copy1.subcomponents = ics_file.subcomponents
copy2 = ics_file.copy()
copy2.subcomponents = ics_file.subcomponents[:]
assert_equal(copy1, copy2)
assert_equal(copy1, ics_file)
assert_equal(copy2, ics_file)
@ -56,20 +78,15 @@ def test_copy_does_not_copy_subcomponents(calendars, tzp):
def test_deep_copies_are_equal(ics_file, tzp):
"""Ensure that deep copies are equal."""
try:
assert_equal(copy.deepcopy(ics_file), copy.deepcopy(ics_file))
except pytz.UnknownTimeZoneError:
# Ignore errors when a custom time zone is used.
# This is still covered by the parsing test.
pass
try:
assert_equal(copy.deepcopy(ics_file), ics_file)
except pytz.UnknownTimeZoneError:
# Ignore errors when a custom time zone is used.
# This is still covered by the parsing test.
pass
"""Ensure that deep copies are equal.
Ignore errors when a custom time zone is used.
This is still covered by the parsing test.
"""
with contextlib.suppress(UnknownTimeZoneError):
assert_equal(copy.deepcopy(ics_file), copy.deepcopy(ics_file))
with contextlib.suppress(UnknownTimeZoneError):
assert_equal(copy.deepcopy(ics_file), ics_file)
def test_vGeo():
@ -80,20 +97,20 @@ def test_vGeo():
def test_vBinary():
assert_equal(vBinary('asd'), vBinary('asd'))
assert_not_equal(vBinary('asdf'), vBinary('asd'))
assert_equal(vBinary("asd"), vBinary("asd"))
assert_not_equal(vBinary("asdf"), vBinary("asd"))
def test_vBoolean():
assert_equal(vBoolean.from_ical('TRUE'), vBoolean.from_ical('TRUE'))
assert_equal(vBoolean.from_ical('FALSE'), vBoolean.from_ical('FALSE'))
assert_not_equal(vBoolean.from_ical('TRUE'), vBoolean.from_ical('FALSE'))
assert_equal(vBoolean.from_ical("TRUE"), vBoolean.from_ical("TRUE"))
assert_equal(vBoolean.from_ical("FALSE"), vBoolean.from_ical("FALSE"))
assert_not_equal(vBoolean.from_ical("TRUE"), vBoolean.from_ical("FALSE"))
def test_vCategory():
assert_equal(vCategory("HELLO"), vCategory("HELLO"))
assert_equal(vCategory(["a","b"]), vCategory(["a","b"]))
assert_not_equal(vCategory(["a","b"]), vCategory(["a","b", "c"]))
assert_equal(vCategory(["a", "b"]), vCategory(["a", "b"]))
assert_not_equal(vCategory(["a", "b"]), vCategory(["a", "b", "c"]))
def test_vText():
@ -102,21 +119,33 @@ def test_vText():
@pytest.mark.parametrize(
"vType,v1,v2",
("vType", "v1", "v2"),
[
(vDatetime, datetime(2023, 11, 1, 10, 11), datetime(2023, 11, 1, 10, 10)),
(vDate, date(2023, 11, 1), date(2023, 10, 31)),
(vDuration, timedelta(3, 11, 1), timedelta(23, 10, 31)),
(vPeriod, (datetime(2023, 11, 1, 10, 11), timedelta(3, 11, 1)), (datetime(2023, 11, 1, 10, 11), timedelta(23, 10, 31))),
(vPeriod, (datetime(2023, 11, 1, 10, 1), timedelta(3, 11, 1)), (datetime(2023, 11, 1, 10, 11), timedelta(3, 11, 1))),
(vPeriod, (datetime(2023, 11, 1, 10, 1), datetime(2023, 11, 1, 10, 3)), (datetime(2023, 11, 1, 10, 1), datetime(2023, 11, 1, 10, 2))),
(
vPeriod,
(datetime(2023, 11, 1, 10, 11), timedelta(3, 11, 1)),
(datetime(2023, 11, 1, 10, 11), timedelta(23, 10, 31)),
),
(
vPeriod,
(datetime(2023, 11, 1, 10, 1), timedelta(3, 11, 1)),
(datetime(2023, 11, 1, 10, 11), timedelta(3, 11, 1)),
),
(
vPeriod,
(datetime(2023, 11, 1, 10, 1), datetime(2023, 11, 1, 10, 3)),
(datetime(2023, 11, 1, 10, 1), datetime(2023, 11, 1, 10, 2)),
),
(vTime, time(10, 10, 10), time(10, 10, 11)),
]
],
)
@pytest.mark.parametrize("eq", ["==", "!="])
@pytest.mark.parametrize("cls1", [0, 1])
@pytest.mark.parametrize("cls2", [0, 1])
@pytest.mark.parametrize("hash", [lambda x:x, hash])
@pytest.mark.parametrize("hash", [lambda x: x, hash])
def test_vDDDTypes_and_others(vType, v1, v2, cls1, cls2, eq, hash):
"""Check equality and inequality."""
t1 = (vType, vDDDTypes)[cls1]
@ -124,7 +153,7 @@ def test_vDDDTypes_and_others(vType, v1, v2, cls1, cls2, eq, hash):
if eq == "==":
assert hash(v1) == hash(v1)
assert hash(t1(v1)) == hash(t2(v1))
assert not hash(t1(v1)) != hash(t2(v1))
assert hash(t1(v1)) == hash(t2(v1))
else:
assert hash(v1) != hash(v2)
assert hash(t1(v1)) != hash(t2(v2))
@ -134,11 +163,13 @@ def test_repr_vDDDTypes():
assert "vDDDTypes" in repr(vDDDTypes(timedelta(3, 11, 1)))
vDDDLists_examples = [
vDDDLists_examples = [ # noqa: N816
vDDDLists([]),
vDDDLists([datetime(2023, 11, 1, 10, 1)]),
vDDDLists([datetime(2023, 11, 1, 10, 1), date(2023, 11, 1)]),
]
@pytest.mark.parametrize("l1", vDDDLists_examples)
@pytest.mark.parametrize("l2", vDDDLists_examples)
def test_vDDDLists(l1, l2):

Wyświetl plik

@ -1,51 +1,54 @@
'''tests ensuring that *the* way of doing things works'''
"""tests ensuring that *the* way of doing things works"""
import datetime
from icalendar import Calendar, Event, Timezone
import pytest
from icalendar import Calendar, Event, Timezone
def test_creating_calendar_with_unicode_fields(calendars, utc):
''' create a calendar with events that contain unicode characters in their fields '''
"""create a calendar with events that contain unicode characters in their fields"""
cal = Calendar()
cal.add('PRODID', '-//Plönë.org//NONSGML plone.app.event//EN')
cal.add('VERSION', '2.0')
cal.add('X-WR-CALNAME', 'äöü ÄÖÜ €')
cal.add('X-WR-CALDESC', 'test non ascii: äöü ÄÖÜ €')
cal.add('X-WR-RELCALID', '12345')
cal.add("PRODID", "-//Plönë.org//NONSGML plone.app.event//EN")
cal.add("VERSION", "2.0")
cal.add("X-WR-CALNAME", "äöü ÄÖÜ €")
cal.add("X-WR-CALDESC", "test non ascii: äöü ÄÖÜ €")
cal.add("X-WR-RELCALID", "12345")
event = Event()
event.add('DTSTART', datetime.datetime(2010, 10, 10, 10, 0, 0, tzinfo=utc))
event.add('DTEND', datetime.datetime(2010, 10, 10, 12, 0, 0, tzinfo=utc))
event.add('CREATED', datetime.datetime(2010, 10, 10, 0, 0, 0, tzinfo=utc))
event.add('UID', '123456')
event.add('SUMMARY', 'Non-ASCII Test: ÄÖÜ äöü €')
event.add('DESCRIPTION', 'icalendar should be able to de/serialize non-ascii.')
event.add('LOCATION', 'Tribstrül')
event.add("DTSTART", datetime.datetime(2010, 10, 10, 10, 0, 0, tzinfo=utc))
event.add("DTEND", datetime.datetime(2010, 10, 10, 12, 0, 0, tzinfo=utc))
event.add("CREATED", datetime.datetime(2010, 10, 10, 0, 0, 0, tzinfo=utc))
event.add("UID", "123456")
event.add("SUMMARY", "Non-ASCII Test: ÄÖÜ äöü €")
event.add("DESCRIPTION", "icalendar should be able to de/serialize non-ascii.")
event.add("LOCATION", "Tribstrül")
cal.add_component(event)
# test_create_event_simple
event1 = Event()
event1.add('DTSTART', datetime.datetime(2010, 10, 10, 0, 0, 0, tzinfo=utc))
event1.add('SUMMARY', 'åäö')
event1.add("DTSTART", datetime.datetime(2010, 10, 10, 0, 0, 0, tzinfo=utc))
event1.add("SUMMARY", "åäö")
cal.add_component(event1)
# test_unicode_parameter_name
# test for issue #80 https://github.com/collective/icalendar/issues/80
event2 = Event()
event2.add('DESCRIPTION', 'äöüßÄÖÜ')
event2.add("DESCRIPTION", "äöüßÄÖÜ")
cal.add_component(event2)
assert cal.to_ical() == calendars.created_calendar_with_unicode_fields.raw_ics
@pytest.mark.parametrize("component,example",
@pytest.mark.parametrize(
("component", "example"),
[
(Calendar, "example"),
(Calendar, "example.ics"),
(Event, "event_with_rsvp"),
(Timezone, "pacific_fiji"),
]
],
)
def test_component_has_examples(tzp, calendars, timezones, events, component, example):
"""Check that the examples function works."""

Wyświetl plik

@ -1,59 +1,73 @@
from ..parser import Contentlines, Contentline, Parameters, foldline
from ..parser import q_join, q_split, dquote
from ..prop import vText
import unittest
from icalendar.parser import (
Contentline,
Contentlines,
Parameters,
dquote,
foldline,
q_join,
q_split,
)
from icalendar.prop import vText
class IcalendarTestCase (unittest.TestCase):
class IcalendarTestCase(unittest.TestCase):
def setUp(self):
if not hasattr(self, 'assertRaisesRegex'):
if not hasattr(self, "assertRaisesRegex"):
self.assertRaisesRegex = self.assertRaisesRegexp
def test_long_lines(self):
c = Contentlines([Contentline('BEGIN:VEVENT')])
c.append(Contentline(''.join('123456789 ' * 10)))
c = Contentlines([Contentline("BEGIN:VEVENT")])
c.append(Contentline("".join("123456789 " * 10)))
self.assertEqual(
c.to_ical(),
b'BEGIN:VEVENT\r\n123456789 123456789 123456789 123456789 '
b'123456789 123456789 123456789 1234\r\n 56789 123456789 '
b'123456789 \r\n'
b"BEGIN:VEVENT\r\n123456789 123456789 123456789 123456789 "
b"123456789 123456789 123456789 1234\r\n 56789 123456789 "
b"123456789 \r\n",
)
# from doctests
# Notice that there is an extra empty string in the end of the content
# lines. That is so they can be easily joined with:
# '\r\n'.join(contentlines))
self.assertEqual(Contentlines.from_ical('A short line\r\n'),
['A short line', ''])
self.assertEqual(Contentlines.from_ical('A faked\r\n long line\r\n'),
['A faked long line', ''])
self.assertEqual(
Contentlines.from_ical('A faked\r\n long line\r\nAnd another '
'lin\r\n\te that is folded\r\n'),
['A faked long line', 'And another line that is folded', '']
Contentlines.from_ical("A short line\r\n"), ["A short line", ""]
)
self.assertEqual(
Contentlines.from_ical("A faked\r\n long line\r\n"),
["A faked long line", ""],
)
self.assertEqual(
Contentlines.from_ical(
"A faked\r\n long line\r\nAnd another " "lin\r\n\te that is folded\r\n"
),
["A faked long line", "And another line that is folded", ""],
)
def test_contentline_class(self):
self.assertEqual(
Contentline('Si meliora dies, ut vina, poemata reddit').to_ical(),
b'Si meliora dies, ut vina, poemata reddit'
Contentline("Si meliora dies, ut vina, poemata reddit").to_ical(),
b"Si meliora dies, ut vina, poemata reddit",
)
# A long line gets folded
c = Contentline(''.join(['123456789 '] * 10)).to_ical()
c = Contentline("".join(["123456789 "] * 10)).to_ical()
self.assertEqual(
c,
(b'123456789 123456789 123456789 123456789 123456789 123456789 '
b'123456789 1234\r\n 56789 123456789 123456789 ')
(
b"123456789 123456789 123456789 123456789 123456789 123456789 "
b"123456789 1234\r\n 56789 123456789 123456789 "
),
)
# A folded line gets unfolded
self.assertEqual(
Contentline.from_ical(c),
('123456789 123456789 123456789 123456789 123456789 123456789 '
'123456789 123456789 123456789 123456789 ')
(
"123456789 123456789 123456789 123456789 123456789 123456789 "
"123456789 123456789 123456789 123456789 "
),
)
# https://tools.ietf.org/html/rfc5545#section-3.3.11
@ -63,219 +77,228 @@ class IcalendarTestCase (unittest.TestCase):
# N or a LATIN CAPITAL LETTER N, that is "\n" or "\N".
# Newlines are not allowed in content lines
self.assertRaises(AssertionError, Contentline, b'1234\r\n\r\n1234')
self.assertRaises(AssertionError, Contentline, b"1234\r\n\r\n1234")
self.assertEqual(
Contentline('1234\\n\\n1234').to_ical(),
b'1234\\n\\n1234'
)
self.assertEqual(Contentline("1234\\n\\n1234").to_ical(), b"1234\\n\\n1234")
# We do not fold within a UTF-8 character
c = Contentline(b'This line has a UTF-8 character where it should be '
b'folded. Make sure it g\xc3\xabts folded before that '
b'character.')
c = Contentline(
b"This line has a UTF-8 character where it should be "
b"folded. Make sure it g\xc3\xabts folded before that "
b"character."
)
self.assertIn(b'\xc3\xab', c.to_ical())
self.assertIn(b"\xc3\xab", c.to_ical())
# Another test of the above
c = Contentline(b'x' * 73 + b'\xc3\xab' + b'\\n ' + b'y' * 10)
c = Contentline(b"x" * 73 + b"\xc3\xab" + b"\\n " + b"y" * 10)
self.assertEqual(c.to_ical().count(b'\xc3'), 1)
self.assertEqual(c.to_ical().count(b"\xc3"), 1)
# Don't fail if we fold a line that is exactly X times 74 characters
# long
c = Contentline(''.join(['x'] * 148)).to_ical()
c = Contentline("".join(["x"] * 148)).to_ical()
# It can parse itself into parts,
# which is a tuple of (name, params, vals)
self.assertEqual(
Contentline('dtstart:20050101T120000').parts(),
('dtstart', Parameters({}), '20050101T120000')
Contentline("dtstart:20050101T120000").parts(),
("dtstart", Parameters({}), "20050101T120000"),
)
self.assertEqual(
Contentline('dtstart;value=datetime:20050101T120000').parts(),
('dtstart', Parameters({'VALUE': 'datetime'}), '20050101T120000')
Contentline("dtstart;value=datetime:20050101T120000").parts(),
("dtstart", Parameters({"VALUE": "datetime"}), "20050101T120000"),
)
c = Contentline('ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:'
'MAILTO:maxm@example.com')
c = Contentline(
"ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:" "MAILTO:maxm@example.com"
)
self.assertEqual(
c.parts(),
('ATTENDEE',
Parameters({'ROLE': 'REQ-PARTICIPANT', 'CN': 'Max Rasmussen'}),
'MAILTO:maxm@example.com')
(
"ATTENDEE",
Parameters({"ROLE": "REQ-PARTICIPANT", "CN": "Max Rasmussen"}),
"MAILTO:maxm@example.com",
),
)
self.assertEqual(
c.to_ical().decode('utf-8'),
'ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:'
'MAILTO:maxm@example.com'
c.to_ical().decode("utf-8"),
"ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:" "MAILTO:maxm@example.com",
)
# and back again
# NOTE: we are quoting property values with spaces in it.
parts = ('ATTENDEE',
Parameters({'ROLE': 'REQ-PARTICIPANT',
'CN': 'Max Rasmussen'}),
'MAILTO:maxm@example.com')
parts = (
"ATTENDEE",
Parameters({"ROLE": "REQ-PARTICIPANT", "CN": "Max Rasmussen"}),
"MAILTO:maxm@example.com",
)
self.assertEqual(
Contentline.from_parts(*parts),
'ATTENDEE;CN="Max Rasmussen";ROLE=REQ-PARTICIPANT:'
'MAILTO:maxm@example.com'
"MAILTO:maxm@example.com",
)
# and again
parts = ('ATTENDEE', Parameters(), 'MAILTO:maxm@example.com')
parts = ("ATTENDEE", Parameters(), "MAILTO:maxm@example.com")
self.assertEqual(
Contentline.from_parts(*parts),
'ATTENDEE:MAILTO:maxm@example.com'
Contentline.from_parts(*parts), "ATTENDEE:MAILTO:maxm@example.com"
)
# A value can also be any of the types defined in PropertyValues
parts = ('ATTENDEE', Parameters(), vText('MAILTO:test@example.com'))
parts = ("ATTENDEE", Parameters(), vText("MAILTO:test@example.com"))
self.assertEqual(
Contentline.from_parts(*parts),
'ATTENDEE:MAILTO:test@example.com'
Contentline.from_parts(*parts), "ATTENDEE:MAILTO:test@example.com"
)
# A value in UTF-8
parts = ('SUMMARY', Parameters(), vText('INternational char æ ø å'))
parts = ("SUMMARY", Parameters(), vText("INternational char æ ø å"))
self.assertEqual(
Contentline.from_parts(*parts),
'SUMMARY:INternational char æ ø å'
Contentline.from_parts(*parts), "SUMMARY:INternational char æ ø å"
)
# A value can also be unicode
parts = ('SUMMARY', Parameters(), vText('INternational char æ ø å'))
parts = ("SUMMARY", Parameters(), vText("INternational char æ ø å"))
self.assertEqual(
Contentline.from_parts(*parts),
'SUMMARY:INternational char æ ø å'
Contentline.from_parts(*parts), "SUMMARY:INternational char æ ø å"
)
# Traversing could look like this.
name, params, vals = c.parts()
self.assertEqual(name, 'ATTENDEE')
self.assertEqual(vals, 'MAILTO:maxm@example.com')
self.assertEqual(name, "ATTENDEE")
self.assertEqual(vals, "MAILTO:maxm@example.com")
self.assertEqual(
sorted(params.items()),
sorted([('ROLE', 'REQ-PARTICIPANT'), ('CN', 'Max Rasmussen')])
sorted([("ROLE", "REQ-PARTICIPANT"), ("CN", "Max Rasmussen")]),
)
# And the traditional failure
with self.assertRaisesRegex(
ValueError,
'Content line could not be parsed into parts'
ValueError, "Content line could not be parsed into parts"
):
Contentline('ATTENDEE;maxm@example.com').parts()
Contentline("ATTENDEE;maxm@example.com").parts()
# Another failure:
with self.assertRaisesRegex(
ValueError,
'Content line could not be parsed into parts'
ValueError, "Content line could not be parsed into parts"
):
Contentline(':maxm@example.com').parts()
Contentline(":maxm@example.com").parts()
self.assertEqual(
Contentline('key;param=:value').parts(),
('key', Parameters({'PARAM': ''}), 'value')
Contentline("key;param=:value").parts(),
("key", Parameters({"PARAM": ""}), "value"),
)
self.assertEqual(
Contentline('key;param="pvalue":value').parts(),
('key', Parameters({'PARAM': 'pvalue'}), 'value')
("key", Parameters({"PARAM": "pvalue"}), "value"),
)
# Should bomb on missing param:
with self.assertRaisesRegex(
ValueError,
'Content line could not be parsed into parts'
ValueError, "Content line could not be parsed into parts"
):
Contentline.from_ical("k;:no param").parts()
self.assertEqual(
Contentline('key;param=pvalue:value', strict=False).parts(),
('key', Parameters({'PARAM': 'pvalue'}), 'value')
Contentline("key;param=pvalue:value", strict=False).parts(),
("key", Parameters({"PARAM": "pvalue"}), "value"),
)
# If strict is set to True, uppercase param values that are not
# double-quoted, this is because the spec says non-quoted params are
# case-insensitive.
self.assertEqual(
Contentline('key;param=pvalue:value', strict=True).parts(),
('key', Parameters({'PARAM': 'PVALUE'}), 'value')
Contentline("key;param=pvalue:value", strict=True).parts(),
("key", Parameters({"PARAM": "PVALUE"}), "value"),
)
self.assertEqual(
Contentline('key;param="pValue":value', strict=True).parts(),
('key', Parameters({'PARAM': 'pValue'}), 'value')
("key", Parameters({"PARAM": "pValue"}), "value"),
)
contains_base64 = (
b'X-APPLE-STRUCTURED-LOCATION;'
b"X-APPLE-STRUCTURED-LOCATION;"
b'VALUE=URI;X-ADDRESS="Kaiserliche Hofburg, 1010 Wien";'
b'X-APPLE-MAPKIT-HANDLE=CAESxQEZgr3QZXJyZWljaA==;'
b'X-APPLE-RADIUS=328.7978217977285;X-APPLE-REFERENCEFRAME=1;'
b'X-TITLE=Heldenplatz:geo:48.206686,16.363235'
b"X-APPLE-MAPKIT-HANDLE=CAESxQEZgr3QZXJyZWljaA==;"
b"X-APPLE-RADIUS=328.7978217977285;X-APPLE-REFERENCEFRAME=1;"
b"X-TITLE=Heldenplatz:geo:48.206686,16.363235"
)
self.assertEqual(
Contentline(contains_base64, strict=True).parts(),
('X-APPLE-STRUCTURED-LOCATION',
Parameters({
'X-APPLE-RADIUS': '328.7978217977285',
'X-ADDRESS': 'Kaiserliche Hofburg, 1010 Wien',
'X-APPLE-REFERENCEFRAME': '1',
'X-TITLE': 'HELDENPLATZ',
'X-APPLE-MAPKIT-HANDLE':
'CAESXQEZGR3QZXJYZWLJAA==',
'VALUE': 'URI',
}),
'geo:48.206686,16.363235'
)
(
"X-APPLE-STRUCTURED-LOCATION",
Parameters(
{
"X-APPLE-RADIUS": "328.7978217977285",
"X-ADDRESS": "Kaiserliche Hofburg, 1010 Wien",
"X-APPLE-REFERENCEFRAME": "1",
"X-TITLE": "HELDENPLATZ",
"X-APPLE-MAPKIT-HANDLE": "CAESXQEZGR3QZXJYZWLJAA==",
"VALUE": "URI",
}
),
"geo:48.206686,16.363235",
),
)
def test_fold_line(self):
self.assertEqual(foldline('foo'), 'foo')
self.assertEqual(foldline("foo"), "foo")
self.assertEqual(
foldline("Lorem ipsum dolor sit amet, consectetur adipiscing "
"elit. Vestibulum convallis imperdiet dui posuere."),
('Lorem ipsum dolor sit amet, consectetur adipiscing elit. '
'Vestibulum conval\r\n lis imperdiet dui posuere.')
foldline(
"Lorem ipsum dolor sit amet, consectetur adipiscing "
"elit. Vestibulum convallis imperdiet dui posuere."
),
(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
"Vestibulum conval\r\n lis imperdiet dui posuere."
),
)
# I don't really get this test
# at least just but bytes in there
# porting it to "run" under python 2 & 3 makes it not much better
with self.assertRaises(AssertionError):
foldline('привет'.encode(), limit=3)
foldline("привет".encode(), limit=3)
self.assertEqual(foldline('foobar', limit=4), 'foo\r\n bar')
self.assertEqual(foldline("foobar", limit=4), "foo\r\n bar")
self.assertEqual(
foldline('Lorem ipsum dolor sit amet, consectetur adipiscing elit'
'. Vestibulum convallis imperdiet dui posuere.'),
('Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
' Vestibulum conval\r\n lis imperdiet dui posuere.')
foldline(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit"
". Vestibulum convallis imperdiet dui posuere."
),
(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
" Vestibulum conval\r\n lis imperdiet dui posuere."
),
)
self.assertEqual(
foldline('DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ'),
'DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭ\r\n ЮЯ'
foldline("DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ"),
"DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭ\r\n ЮЯ",
)
def test_value_double_quoting(self):
self.assertEqual(dquote('Max'), 'Max')
self.assertEqual(dquote('Rasmussen, Max'), '"Rasmussen, Max"')
self.assertEqual(dquote('name:value'), '"name:value"')
self.assertEqual(dquote("Max"), "Max")
self.assertEqual(dquote("Rasmussen, Max"), '"Rasmussen, Max"')
self.assertEqual(dquote("name:value"), '"name:value"')
def test_q_split(self):
self.assertEqual(q_split('Max,Moller,"Rasmussen, Max"'),
['Max', 'Moller', '"Rasmussen, Max"'])
self.assertEqual(
q_split('Max,Moller,"Rasmussen, Max"'),
["Max", "Moller", '"Rasmussen, Max"'],
)
def test_q_split_bin(self):
for s in ('X-SOMETHING=ABCDE==', ',,,'):
for s in ("X-SOMETHING=ABCDE==", ",,,"):
for maxsplit in range(-1, 3):
self.assertEqual(q_split(s, '=', maxsplit=maxsplit),
s.split('=', maxsplit))
self.assertEqual(
q_split(s, "=", maxsplit=maxsplit), s.split("=", maxsplit)
)
def test_q_join(self):
self.assertEqual(q_join(['Max', 'Moller', 'Rasmussen, Max']),
'Max,Moller,"Rasmussen, Max"')
self.assertEqual(
q_join(["Max", "Moller", "Rasmussen, Max"]), 'Max,Moller,"Rasmussen, Max"'
)

Wyświetl plik

@ -1,4 +1,3 @@
import icalendar
@ -15,14 +14,14 @@ def test_issue_116():
"VALUE": "URI",
"X-ADDRESS": "367 George Street Sydney CBD NSW 2000",
"X-APPLE-RADIUS": "72",
"X-TITLE": "367 George Street"
}
"X-TITLE": "367 George Street",
},
)
assert event.to_ical() == (
b'BEGIN:VEVENT\r\nX-APPLE-STRUCTURED-LOCATION;VALUE=URI;'
b"BEGIN:VEVENT\r\nX-APPLE-STRUCTURED-LOCATION;VALUE=URI;"
b'X-ADDRESS="367 George Street Sydney \r\n CBD NSW 2000";'
b'X-APPLE-RADIUS=72;X-TITLE="367 George Street":'
b'geo:-33.868900\r\n \\,151.207000\r\nEND:VEVENT\r\n'
b"geo:-33.868900\r\n \\,151.207000\r\nEND:VEVENT\r\n"
)
# roundtrip

Wyświetl plik

@ -1,8 +1,9 @@
'''Issue #165 - Problem parsing a file with event recurring on weekdays
"""Issue #165 - Problem parsing a file with event recurring on weekdays
https://github.com/collective/icalendar/issues/165
"""
https://github.com/collective/icalendar/issues/165
'''
def test_issue_165_missing_event(calendars):
events = list(calendars.issue_165_missing_event.walk('VEVENT'))
events = list(calendars.issue_165_missing_event.walk("VEVENT"))
assert len(events) == 1, "There was an event missing from the parsed events' list."

Wyświetl plik

@ -1,9 +1,16 @@
'''Issue #168 - Parsing invalid icalendars fails without any warning
"""Issue #168 - Parsing invalid icalendars fails without any warning
https://github.com/collective/icalendar/issues/168
"""
https://github.com/collective/icalendar/issues/168
'''
def test_issue_168_parsing_inavlid_calendars_no_warning(calendars):
expected_error = (None, "Content line could not be parsed into parts: 'X-APPLE-RADIUS=49.91307046514149': X-APPLE-RADIUS=49.91307046514149")
assert expected_error in calendars.issue_168_input.walk('VEVENT')[0].errors
assert calendars.issue_168_input.to_ical() == calendars.issue_168_expected_output.raw_ics
expected_error = (
None,
"Content line could not be parsed into parts: 'X-APPLE-RADIUS=49.91307046514149': X-APPLE-RADIUS=49.91307046514149",
)
assert expected_error in calendars.issue_168_input.walk("VEVENT")[0].errors
assert (
calendars.issue_168_input.to_ical()
== calendars.issue_168_expected_output.raw_ics
)

Wyświetl plik

@ -1,13 +1,19 @@
'''Issue #27 - multiple periods
"""Issue #27 - multiple periods
https://github.com/collective/icalendar/issues/27
"""
https://github.com/collective/icalendar/issues/27
'''
def test_issue_27_multiple_periods(calendars):
free_busy = list(calendars.issue_27_multiple_periods_in_freebusy_multiple_freebusies.walk('VFREEBUSY'))[0]
free_busy_period = free_busy['freebusy']
print(free_busy['freebusy'])
equivalent_way_of_defining_free_busy = list(calendars.issue_27_multiple_periods_in_freebusy_one_freebusy.walk('VFREEBUSY'))[0]
free_busy_period_equivalent = equivalent_way_of_defining_free_busy['freebusy']
free_busy = list(
calendars.issue_27_multiple_periods_in_freebusy_multiple_freebusies.walk(
"VFREEBUSY"
)
)[0]
free_busy_period = free_busy["freebusy"]
print(free_busy["freebusy"])
equivalent_way_of_defining_free_busy = list(
calendars.issue_27_multiple_periods_in_freebusy_one_freebusy.walk("VFREEBUSY")
)[0]
free_busy_period_equivalent = equivalent_way_of_defining_free_busy["freebusy"]
assert free_busy_period == free_busy_period_equivalent

Wyświetl plik

@ -8,20 +8,25 @@ Example:
DTSTART;VALUE=DATE-TIME:20190616T050000Z
equals DTSTART:20190616T050000Z
"""
import pytest
from icalendar import Event
from datetime import datetime
import pytest
@pytest.mark.parametrize("attr", [
"DTSTART",
"DTEND",
"DTSTAMP",
])
from icalendar import Event
@pytest.mark.parametrize(
"attr",
[
"DTSTART",
"DTEND",
"DTSTAMP",
],
)
def test_datetime_in_event(attr):
"""Check that the "VALUE=DATE-TIME" is absent because not needed."""
event = Event()
event.add(attr, datetime(2022, 10, 13, 9, 16, 42))
ics = event.to_ical()
assert b"VALUE=DATE-TIME" not in ics

Wyświetl plik

@ -4,7 +4,7 @@ from icalendar import Calendar, Event
def test_issue_322_single_string_split_into_multiple_categories(calendars):
calendar = Calendar()
event = Event()
event.add('summary', 'Event with bare string as argument for categories')
event.add('categories', "Lecture")
event.add("summary", "Event with bare string as argument for categories")
event.add("categories", "Lecture")
calendar.add_component(event)
assert calendar.to_ical() == calendars.issue_322_expected_calendar.raw_ics

Wyświetl plik

@ -7,7 +7,7 @@ see https://github.com/collective/icalendar/issues/348
def test_calendar_can_be_parsed_correctly(calendars):
"""Exception when there's no ':' when parsing value #348
see https://github.com/collective/icalendar/issues/348
see https://github.com/collective/icalendar/issues/348
"""
freebusy = calendars.issue_348_exception_parsing_value.walk("VFREEBUSY")[0]
assert freebusy["ORGANIZER"].params["CN"] == "Sixt SE"

Wyświetl plik

@ -1,8 +1,9 @@
'''Issue #350 - Ignore X-... properties also at end of file?
"""Issue #350 - Ignore X-... properties also at end of file?
https://github.com/collective/icalendar/issues/350
"""
https://github.com/collective/icalendar/issues/350
'''
def test_issue_350(calendars):
calendar = list(calendars.issue_350.walk('X-COMMENT'))
calendar = list(calendars.issue_350.walk("X-COMMENT"))
assert len(calendar) == 0, "X-COMMENT at the end of the file was parsed"

Wyświetl plik

@ -1,9 +1,10 @@
from icalendar import Event, vBoolean, vCalAddress
def test_vBoolean_can_be_used_as_parameter_issue_500(events):
'''https://github.com/collective/icalendar/issues/500'''
attendee = vCalAddress('mailto:someone@example.com')
attendee.params['rsvp'] = vBoolean(True)
"""https://github.com/collective/icalendar/issues/500"""
attendee = vCalAddress("mailto:someone@example.com")
attendee.params["rsvp"] = vBoolean(True)
event = Event()
event.add('attendee', attendee)
event.add("attendee", attendee)
assert event.to_ical() == events.event_with_rsvp.raw_ics

Wyświetl plik

@ -6,7 +6,6 @@ making its behavior unexpected.
see https://github.com/collective/icalendar/issues/557"""
import unittest
from icalendar.cal import Component

Wyświetl plik

@ -1,6 +1,6 @@
"""An example with multiple VCALENDAR components"""
from icalendar.prop import vText
from icalendar.prop import vText
def test_multiple(calendars):
@ -9,6 +9,8 @@ def test_multiple(calendars):
cals = calendars.multiple.multiple_calendar_components
assert len(cals) == 2
assert [comp.name for comp in cals[0].walk()] == ['VCALENDAR', 'VEVENT']
assert [comp.name for comp in cals[1].walk()] == ['VCALENDAR', 'VEVENT', 'VEVENT']
assert cals[0]['prodid'] == vText('-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN')
assert [comp.name for comp in cals[0].walk()] == ["VCALENDAR", "VEVENT"]
assert [comp.name for comp in cals[1].walk()] == ["VCALENDAR", "VEVENT", "VEVENT"]
assert cals[0]["prodid"] == vText(
"-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN"
)

Wyświetl plik

@ -1,10 +1,12 @@
"""This file collects errors that the OSS FUZZ build has found."""
from datetime import time
from icalendar import Calendar
from icalendar.prop import vDDDLists
import pytest
from icalendar import Calendar
from icalendar.prop import vDDDLists
def test_stack_is_empty():
"""If we get passed an invalid string, we expect to get a ValueError."""
@ -15,4 +17,4 @@ def test_stack_is_empty():
def test_vdd_list_type_mismatch():
"""If we pass in a string type, we expect it to be converted to bytes"""
vddd_list = vDDDLists([time(hour=6, minute=6, second=6)])
assert vddd_list.to_ical() == b'060606'
assert vddd_list.to_ical() == b"060606"

Wyświetl plik

@ -1,194 +1,257 @@
'''Tests checking that parsing works'''
import pytest
"""Tests checking that parsing works"""
import base64
from icalendar import Calendar, vRecur, vBinary, Event
from datetime import datetime
import pytest
from icalendar import Calendar, Event, vBinary, vRecur
from icalendar.parser import Contentline, Parameters, unescape_char
@pytest.mark.parametrize('calendar_name', [
# Issue #178 - A component with an unknown/invalid name is represented
# as one of the known components, the information about the original
# component name is lost.
# https://github.com/collective/icalendar/issues/178 https://github.com/collective/icalendar/pull/180
# Parsing of a nonstandard component
'issue_178_component_with_invalid_name_represented',
# Nonstandard component inside other components, also has properties
'issue_178_custom_component_inside_other',
# Nonstandard component is able to contain other components
'issue_178_custom_component_contains_other',
])
@pytest.mark.parametrize(
"calendar_name",
[
# Issue #178 - A component with an unknown/invalid name is represented
# as one of the known components, the information about the original
# component name is lost.
# https://github.com/collective/icalendar/issues/178 https://github.com/collective/icalendar/pull/180
# Parsing of a nonstandard component
"issue_178_component_with_invalid_name_represented",
# Nonstandard component inside other components, also has properties
"issue_178_custom_component_inside_other",
# Nonstandard component is able to contain other components
"issue_178_custom_component_contains_other",
],
)
def test_calendar_to_ical_is_inverse_of_from_ical(calendars, calendar_name):
calendar = getattr(calendars, calendar_name)
assert calendar.to_ical().splitlines() == calendar.raw_ics.splitlines()
assert calendar.to_ical() == calendar.raw_ics
@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(), \
''))
])
@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')
])
@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
"""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()
"""
assert 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
"""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'
"""
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
"""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'
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'),
# PERIOD should be put back into shape
'issue_156_RDATE_with_PERIOD',
'issue_156_RDATE_with_PERIOD_list',
'event_with_unicode_organizer',
])
@pytest.mark.parametrize(
"event_name",
[
# https://github.com/collective/icalendar/pull/100
("issue_100_transformed_doctests_into_unittests"),
("issue_184_broken_representation_of_period"),
# PERIOD should be put back into shape
"issue_156_RDATE_with_PERIOD",
"issue_156_RDATE_with_PERIOD_list",
"event_with_unicode_organizer",
],
)
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().splitlines() == event.raw_ics.splitlines()
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')
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'
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?
"""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()
"""
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.
"""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")
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?
"""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'
"""
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']
"""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:20120716T000000Z'),
(datetime(2021, 11, 17, 15, 9, 15), b'DTSTART:20211117T150915Z')
])
@pytest.mark.parametrize(
("date", "expected_output"),
[
(datetime(2012, 7, 16, 0, 0, 0), b"DTSTART:20120716T000000Z"),
(datetime(2021, 11, 17, 15, 9, 15), b"DTSTART:20211117T150915Z"),
],
)
def test_no_tzid_when_utc(utc, date, expected_output):
'''Issue #58 - TZID on UTC DATE-TIMEs
"""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 5545: "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)
event.add("dtstart", date)
assert expected_output in event.to_ical()
def test_vBinary_base64_encoded_issue_82():
'''Issue #82 - vBinary __repr__ called rather than to_ical from
"""Issue #82 - vBinary __repr__ called rather than to_ical from
container types
https://github.com/collective/icalendar/issues/82
'''
b = vBinary('text')
b.params['FMTTYPE'] = 'text/plain'
assert b.to_ical() == base64.b64encode(b'text')
"""
b = vBinary("text")
b.params["FMTTYPE"] = "text/plain"
assert b.to_ical() == base64.b64encode(b"text")
def test_creates_event_with_base64_encoded_attachment_issue_82(events):
'''Issue #82 - vBinary __repr__ called rather than to_ical from
"""Issue #82 - vBinary __repr__ called rather than to_ical from
container types
https://github.com/collective/icalendar/issues/82
'''
b = vBinary('text')
b.params['FMTTYPE'] = 'text/plain'
"""
b = vBinary("text")
b.params["FMTTYPE"] = "text/plain"
event = Event()
event.add('ATTACH', b)
event.add("ATTACH", b)
assert event.to_ical() == events.issue_82_expected_output.raw_ics
@pytest.mark.parametrize('calendar_name', [
# Issue #466 - [BUG] TZID timezone is ignored when forward-slash is used
# https://github.com/collective/icalendar/issues/466
'issue_466_respect_unique_timezone',
'issue_466_convert_tzid_with_slash'
])
@pytest.mark.parametrize(
"calendar_name",
[
# Issue #466 - [BUG] TZID timezone is ignored when forward-slash is used
# https://github.com/collective/icalendar/issues/466
"issue_466_respect_unique_timezone",
"issue_466_convert_tzid_with_slash",
],
)
def test_handles_unique_tzid(calendars, in_timezone, calendar_name):
calendar = calendars[calendar_name]
event = calendar.walk('VEVENT')[0]
event = calendar.walk("VEVENT")[0]
print(vars(event))
start_dt = event['dtstart'].dt
end_dt = event['dtend'].dt
assert start_dt == in_timezone(datetime(2022, 10, 21, 20, 0, 0), 'Europe/Stockholm')
assert end_dt == in_timezone(datetime(2022, 10, 21, 21, 0, 0), 'Europe/Stockholm')
start_dt = event["dtstart"].dt
end_dt = event["dtend"].dt
assert start_dt == in_timezone(datetime(2022, 10, 21, 20, 0, 0), "Europe/Stockholm")
assert end_dt == in_timezone(datetime(2022, 10, 21, 21, 0, 0), "Europe/Stockholm")
@pytest.mark.parametrize('event_name, expected_cn, expected_ics', [
('event_with_escaped_characters', r'that, that; %th%%at%\ that:', 'это, то; that\\ %th%%at%:'),
('event_with_escaped_character1', r'Society, 2014', 'that'),
('event_with_escaped_character2', r'Society\ 2014', 'that'),
('event_with_escaped_character3', r'Society; 2014', 'that'),
('event_with_escaped_character4', r'Society: 2014', 'that'),
])
@pytest.mark.parametrize(
("event_name", "expected_cn", "expected_ics"),
[
(
"event_with_escaped_characters",
r"that, that; %th%%at%\ that:",
"это, то; that\\ %th%%at%:",
),
("event_with_escaped_character1", r"Society, 2014", "that"),
("event_with_escaped_character2", r"Society\ 2014", "that"),
("event_with_escaped_character3", r"Society; 2014", "that"),
("event_with_escaped_character4", r"Society: 2014", "that"),
],
)
def test_escaped_characters_read(event_name, expected_cn, expected_ics, events):
event = events[event_name]
assert event['ORGANIZER'].params['CN'] == expected_cn
assert event['ORGANIZER'].to_ical() == expected_ics.encode('utf-8')
assert event["ORGANIZER"].params["CN"] == expected_cn
assert event["ORGANIZER"].to_ical() == expected_ics.encode("utf-8")
def test_unescape_char():
assert unescape_char(b'123') == b'123'
assert unescape_char(b"123") == b"123"
assert unescape_char(b"\\n") == b"\n"

Wyświetl plik

@ -4,29 +4,62 @@ See
- https://github.com/collective/icalendar/issues/156
- https://github.com/pimutils/khal/issues/152#issuecomment-933635248
"""
import pytest
from icalendar.prop import vDDDTypes
import datetime
import pytest
@pytest.mark.parametrize("calname,tzname,index,period_string", [
("issue_156_RDATE_with_PERIOD_TZID_khal_2", "Europe/Berlin", 0, "20211101T160000/20211101T163000"),
("issue_156_RDATE_with_PERIOD_TZID_khal_2", "Europe/Berlin", 1, "20211206T160000/20211206T163000"),
("issue_156_RDATE_with_PERIOD_TZID_khal_2", "Europe/Berlin", 2, "20220103T160000/20220103T163000"),
("issue_156_RDATE_with_PERIOD_TZID_khal_2", "Europe/Berlin", 3, "20220207T160000/20220207T163000"),
] + [
("issue_156_RDATE_with_PERIOD_TZID_khal", "America/Chicago", i, period)
for i, period in enumerate(("20180327T080000/20180327T0"
"90000,20180403T080000/20180403T090000,20180410T080000/20180410T090000,2018"
"0417T080000/20180417T090000,20180424T080000/20180424T090000,20180501T08000"
"0/20180501T090000,20180508T080000/20180508T090000,20180515T080000/20180515"
"T090000,20180522T080000/20180522T090000,20180529T080000/20180529T090000,20"
"180605T080000/20180605T090000,20180612T080000/20180612T090000,20180619T080"
"000/20180619T090000,20180626T080000/20180626T090000,20180703T080000/201807"
"03T090000,20180710T080000/20180710T090000,20180717T080000/20180717T090000,"
"20180724T080000/20180724T090000,20180731T080000/20180731T090000").split(","))
])
def test_issue_156_period_list_in_rdate(calendars, calname, tzname, index, period_string):
from icalendar.prop import vDDDTypes
@pytest.mark.parametrize(
("calname", "tzname", "index", "period_string"),
[
(
"issue_156_RDATE_with_PERIOD_TZID_khal_2",
"Europe/Berlin",
0,
"20211101T160000/20211101T163000",
),
(
"issue_156_RDATE_with_PERIOD_TZID_khal_2",
"Europe/Berlin",
1,
"20211206T160000/20211206T163000",
),
(
"issue_156_RDATE_with_PERIOD_TZID_khal_2",
"Europe/Berlin",
2,
"20220103T160000/20220103T163000",
),
(
"issue_156_RDATE_with_PERIOD_TZID_khal_2",
"Europe/Berlin",
3,
"20220207T160000/20220207T163000",
),
]
+ [
("issue_156_RDATE_with_PERIOD_TZID_khal", "America/Chicago", i, period)
for i, period in enumerate(
(
"20180327T080000/20180327T0"
"90000,20180403T080000/20180403T090000,20180410T080000/20180410T090000,2018"
"0417T080000/20180417T090000,20180424T080000/20180424T090000,20180501T08000"
"0/20180501T090000,20180508T080000/20180508T090000,20180515T080000/20180515"
"T090000,20180522T080000/20180522T090000,20180529T080000/20180529T090000,20"
"180605T080000/20180605T090000,20180612T080000/20180612T090000,20180619T080"
"000/20180619T090000,20180626T080000/20180626T090000,20180703T080000/201807"
"03T090000,20180710T080000/20180710T090000,20180717T080000/20180717T090000,"
"20180724T080000/20180724T090000,20180731T080000/20180731T090000"
).split(",")
)
],
)
def test_issue_156_period_list_in_rdate(
calendars, calname, tzname, index, period_string
):
"""Check items in a list of period values."""
calendar = calendars[calname]
rdate = calendar.walk("vevent")[0]["rdate"]
@ -58,5 +91,7 @@ def test_tzid_is_part_of_the_period_values(calendars, tzp):
"""The TZID should be set in the datetime."""
event = list(calendars.period_with_timezone.walk("VEVENT"))[0]
start, end = event["RDATE"].dts[0].dt
assert start == tzp.localize(datetime.datetime(2023, 12, 13, 12), "America/Vancouver")
assert start == tzp.localize(
datetime.datetime(2023, 12, 13, 12), "America/Vancouver"
)
assert end == tzp.localize(datetime.datetime(2023, 12, 13, 15), "America/Vancouver")

Wyświetl plik

@ -1,94 +1,129 @@
import pytest
from icalendar import Calendar, Event, Parameters, vCalAddress
import re
@pytest.mark.parametrize('parameter, expected', [
# Simple parameter:value pair
(Parameters(parameter1='Value1'), b'PARAMETER1=Value1'),
# Parameter with list of values must be separated by comma
(Parameters({'parameter1': ['Value1', 'Value2']}), b'PARAMETER1=Value1,Value2'),
# Multiple parameters must be separated by a semicolon
(Parameters({'RSVP': 'TRUE', 'ROLE': 'REQ-PARTICIPANT'}), b'ROLE=REQ-PARTICIPANT;RSVP=TRUE'),
# Parameter values containing ',;:' must be double quoted
(Parameters({'ALTREP': 'http://www.wiz.org'}), b'ALTREP="http://www.wiz.org"'),
# list items must be quoted separately
(Parameters({'MEMBER': ['MAILTO:projectA@host.com',
'MAILTO:projectB@host.com']}),
b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"'),
(Parameters({'parameter1': 'Value1',
'parameter2': ['Value2', 'Value3'],
'ALTREP': ['http://www.wiz.org', 'value4']}),
b'ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;PARAMETER2=Value2,Value3'),
# Including empty strings
(Parameters({'PARAM': ''}), b'PARAM='),
# We can also parse parameter strings
(Parameters({'MEMBER': ['MAILTO:projectA@host.com',
'MAILTO:projectB@host.com']}),
b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"'),
# We can also parse parameter strings
(Parameters({'PARAMETER1': 'Value1',
'ALTREP': ['http://www.wiz.org', 'value4'],
'PARAMETER2': ['Value2', 'Value3']}),
b'ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;PARAMETER2=Value2,Value3'),
])
import pytest
from icalendar import Calendar, Event, Parameters, vCalAddress
@pytest.mark.parametrize(
("parameter", "expected"),
[
# Simple parameter:value pair
(Parameters(parameter1="Value1"), b"PARAMETER1=Value1"),
# Parameter with list of values must be separated by comma
(Parameters({"parameter1": ["Value1", "Value2"]}), b"PARAMETER1=Value1,Value2"),
# Multiple parameters must be separated by a semicolon
(
Parameters({"RSVP": "TRUE", "ROLE": "REQ-PARTICIPANT"}),
b"ROLE=REQ-PARTICIPANT;RSVP=TRUE",
),
# Parameter values containing ',;:' must be double quoted
(Parameters({"ALTREP": "http://www.wiz.org"}), b'ALTREP="http://www.wiz.org"'),
# list items must be quoted separately
(
Parameters(
{"MEMBER": ["MAILTO:projectA@host.com", "MAILTO:projectB@host.com"]}
),
b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"',
),
(
Parameters(
{
"parameter1": "Value1",
"parameter2": ["Value2", "Value3"],
"ALTREP": ["http://www.wiz.org", "value4"],
}
),
b'ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;PARAMETER2=Value2,Value3',
),
# Including empty strings
(Parameters({"PARAM": ""}), b"PARAM="),
# We can also parse parameter strings
(
Parameters(
{"MEMBER": ["MAILTO:projectA@host.com", "MAILTO:projectB@host.com"]}
),
b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"',
),
# We can also parse parameter strings
(
Parameters(
{
"PARAMETER1": "Value1",
"ALTREP": ["http://www.wiz.org", "value4"],
"PARAMETER2": ["Value2", "Value3"],
}
),
b'ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;PARAMETER2=Value2,Value3',
),
],
)
def test_parameter_to_ical_is_inverse_of_from_ical(parameter, expected):
assert parameter.to_ical() == expected
assert Parameters.from_ical(expected.decode('utf-8')) == parameter
assert Parameters.from_ical(expected.decode("utf-8")) == parameter
def test_parse_parameter_string_without_quotes():
assert Parameters.from_ical('PARAM1=Value 1;PARA2=Value 2') == Parameters({'PARAM1': 'Value 1', 'PARA2': 'Value 2'})
assert Parameters.from_ical("PARAM1=Value 1;PARA2=Value 2") == Parameters(
{"PARAM1": "Value 1", "PARA2": "Value 2"}
)
def test_parametr_is_case_insensitive():
parameter = Parameters(parameter1='Value1')
assert parameter['parameter1'] == parameter['PARAMETER1'] == parameter['PaRaMeTer1']
parameter = Parameters(parameter1="Value1")
assert parameter["parameter1"] == parameter["PARAMETER1"] == parameter["PaRaMeTer1"]
def test_parameter_keys_are_uppercase():
parameter = Parameters(parameter1='Value1')
assert list(parameter.keys()) == ['PARAMETER1']
parameter = Parameters(parameter1="Value1")
assert list(parameter.keys()) == ["PARAMETER1"]
@pytest.mark.parametrize('cn_param, cn_quoted', [
# not double-quoted
('Aramis', 'Aramis'),
# if a space is present - enclose in double quotes
('Aramis Alameda', '"Aramis Alameda"'),
# a single quote in parameter value - double quote the value
('Aramis d\'Alameda', '"Aramis d\'Alameda"'),
('Арамис д\'Аламеда', '"Арамис д\'Аламеда"'),
# double quote is replaced with single quote
('Aramis d\"Alameda', '"Aramis d\'Alameda"'),
])
@pytest.mark.parametrize(
("cn_param", "cn_quoted"),
[
# not double-quoted
("Aramis", "Aramis"),
# if a space is present - enclose in double quotes
("Aramis Alameda", '"Aramis Alameda"'),
# a single quote in parameter value - double quote the value
("Aramis d'Alameda", '"Aramis d\'Alameda"'),
("Арамис д'Аламеда", '"Арамис д\'Аламеда"'),
# double quote is replaced with single quote
('Aramis d"Alameda', '"Aramis d\'Alameda"'),
],
)
def test_quoting(cn_param, cn_quoted):
event = Event()
attendee = vCalAddress('test@example.com')
attendee.params['CN'] = cn_param
event.add('ATTENDEE', attendee)
assert f'ATTENDEE;CN={cn_quoted}:test@example.com' in event.to_ical().decode('utf-8')
attendee = vCalAddress("test@example.com")
attendee.params["CN"] = cn_param
event.add("ATTENDEE", attendee)
assert f"ATTENDEE;CN={cn_quoted}:test@example.com" in event.to_ical().decode(
"utf-8"
)
def test_property_params():
"""Property parameters with values containing a COLON character, a
SEMICOLON character or a COMMA character MUST be placed in quoted
text."""
cal_address = vCalAddress('mailto:john.doe@example.org')
cal_address = vCalAddress("mailto:john.doe@example.org")
cal_address.params["CN"] = "Doe, John"
ical = Calendar()
ical.add('organizer', cal_address)
ical.add("organizer", cal_address)
ical_str = Calendar.to_ical(ical)
exp_str = b"""BEGIN:VCALENDAR\r\nORGANIZER;CN="Doe, John":"""\
b"""mailto:john.doe@example.org\r\nEND:VCALENDAR\r\n"""
exp_str = (
b"""BEGIN:VCALENDAR\r\nORGANIZER;CN="Doe, John":"""
b"""mailto:john.doe@example.org\r\nEND:VCALENDAR\r\n"""
)
assert ical_str == exp_str
# other way around: ensure the property parameters can be restored from
# an icalendar string.
ical2 = Calendar.from_ical(ical_str)
assert ical2.get('ORGANIZER').params.get('CN') == 'Doe, John'
assert ical2.get("ORGANIZER").params.get("CN") == "Doe, John"
def test_parse_and_access_property_params(calendars):
@ -97,13 +132,13 @@ def test_parse_and_access_property_params(calendars):
"""
event = calendars.property_params.walk("VEVENT")[0]
attendee = event['attendee'][0]
assert attendee.to_ical() == b'MAILTO:rembrand@xs4all.nl'
assert attendee.params.to_ical() == b'CN=RembrandXS;PARTSTAT=NEEDS-ACTION;RSVP=TRUE'
assert attendee.params['cn'] == 'RembrandXS'
attendee = event["attendee"][0]
assert attendee.to_ical() == b"MAILTO:rembrand@xs4all.nl"
assert attendee.params.to_ical() == b"CN=RembrandXS;PARTSTAT=NEEDS-ACTION;RSVP=TRUE"
assert attendee.params["cn"] == "RembrandXS"
def test_repr():
"""Test correct class representation.
"""
it = Parameters(parameter1='Value1')
"""Test correct class representation."""
it = Parameters(parameter1="Value1")
assert re.match(r"Parameters\({u?'PARAMETER1': u?'Value1'}\)", str(it))

Wyświetl plik

@ -2,19 +2,23 @@
These are mostly located in icalendar.timezone.
"""
try:
import pytz
from icalendar.timezone.pytz import PYTZ
except ImportError:
pytz = None
from icalendar.timezone.zoneinfo import zoneinfo, ZONEINFO
from dateutil.tz.tz import _tzicalvtz
import pytest
import copy
import pickle
from dateutil.rrule import rrule, MONTHLY
from datetime import datetime
import pytest
from dateutil.rrule import MONTHLY, rrule
from dateutil.tz.tz import _tzicalvtz
from icalendar.timezone.zoneinfo import ZONEINFO, zoneinfo
if pytz:
PYTZ_TIMEZONES = pytz.all_timezones
TZP_ = [PYTZ(), ZONEINFO()]
@ -25,17 +29,27 @@ else:
NEW_TZP_NAME = ["zoneinfo"]
@pytest.mark.parametrize("tz_name", PYTZ_TIMEZONES + list(zoneinfo.available_timezones()))
@pytest.mark.parametrize(
"tz_name", PYTZ_TIMEZONES + list(zoneinfo.available_timezones())
)
@pytest.mark.parametrize("tzp_", TZP_)
def test_timezone_names_are_known(tz_name, tzp_):
"""Make sure that all timezones are understood."""
if tz_name in ("Factory", "localtime"):
pytest.skip()
assert tzp_.knows_timezone_id(tz_name), f"{tzp_.__class__.__name__} should know {tz_name}"
assert tzp_.knows_timezone_id(
tz_name
), f"{tzp_.__class__.__name__} should know {tz_name}"
@pytest.mark.parametrize("func", [pickle.dumps, copy.copy, copy.deepcopy])
@pytest.mark.parametrize("obj", [_tzicalvtz("id"), rrule(freq=MONTHLY, count=4, dtstart=datetime(2028, 10, 1), cache=True)])
@pytest.mark.parametrize(
"obj",
[
_tzicalvtz("id"),
rrule(freq=MONTHLY, count=4, dtstart=datetime(2028, 10, 1), cache=True),
],
)
def test_can_pickle_timezone(func, tzp, obj):
"""Check that we can serialize and copy timezones."""
func(obj)

Wyświetl plik

@ -1,22 +1,32 @@
from icalendar import Event
from datetime import date, datetime
import pytest
def test_recurrence_properly_parsed(events):
assert events.event_with_recurrence['rrule'] == {'COUNT': [100], 'FREQ': ['DAILY']}
from icalendar import Event
@pytest.mark.parametrize('i, exception_date', [
(0, datetime(1996, 4, 2, 1, 0)),
(1, datetime(1996, 4, 3, 1, 0)),
(2, datetime(1996, 4, 4, 1, 0))
])
def test_recurrence_properly_parsed(events):
assert events.event_with_recurrence["rrule"] == {"COUNT": [100], "FREQ": ["DAILY"]}
@pytest.mark.parametrize(
("i", "exception_date"),
[
(0, datetime(1996, 4, 2, 1, 0)),
(1, datetime(1996, 4, 3, 1, 0)),
(2, datetime(1996, 4, 4, 1, 0)),
],
)
def test_exdate_properly_parsed(events, i, exception_date, in_timezone):
assert events.event_with_recurrence['exdate'].dts[i].dt == in_timezone(exception_date, 'UTC')
assert events.event_with_recurrence["exdate"].dts[i].dt == in_timezone(
exception_date, "UTC"
)
def test_exdate_properly_marshalled(events):
actual = events.event_with_recurrence['exdate'].to_ical()
assert actual == b'19960402T010000Z,19960403T010000Z,19960404T010000Z'
actual = events.event_with_recurrence["exdate"].to_ical()
assert actual == b"19960402T010000Z,19960403T010000Z,19960404T010000Z"
# TODO: DOCUMENT BETTER!
# In this case we have multiple EXDATE definitions, one per line.
@ -26,49 +36,95 @@ def test_exdate_properly_marshalled(events):
# code has to handle this as list and not blindly expecting to be able
# to call event['EXDATE'].to_ical() on it:
def test_exdate_formed_from_exdates_on_multiple_lines_is_a_list(events):
exdate = events.event_with_recurrence_exdates_on_different_lines['exdate']
exdate = events.event_with_recurrence_exdates_on_different_lines["exdate"]
assert isinstance(exdate, list)
@pytest.mark.parametrize('i, exception_date, exception_date_ics', [
(0, datetime(2012, 5, 29, 10, 0), b'20120529T100000'),
(1, datetime(2012, 4, 3, 10, 0), b'20120403T100000'),
(2, datetime(2012, 4, 10, 10, 0), b'20120410T100000'),
(3, datetime(2012, 5, 1, 10, 0), b'20120501T100000'),
(4, datetime(2012, 4, 17, 10, 0), b'20120417T100000')
])
def test_list_exdate_to_ical_is_inverse_of_from_ical(events, i, exception_date, exception_date_ics, in_timezone):
exdate = events.event_with_recurrence_exdates_on_different_lines['exdate']
assert exdate[i].dts[0].dt == in_timezone(exception_date, 'Europe/Vienna')
@pytest.mark.parametrize(
("i", "exception_date", "exception_date_ics"),
[
(0, datetime(2012, 5, 29, 10, 0), b"20120529T100000"),
(1, datetime(2012, 4, 3, 10, 0), b"20120403T100000"),
(2, datetime(2012, 4, 10, 10, 0), b"20120410T100000"),
(3, datetime(2012, 5, 1, 10, 0), b"20120501T100000"),
(4, datetime(2012, 4, 17, 10, 0), b"20120417T100000"),
],
)
def test_list_exdate_to_ical_is_inverse_of_from_ical(
events, i, exception_date, exception_date_ics, in_timezone
):
exdate = events.event_with_recurrence_exdates_on_different_lines["exdate"]
assert exdate[i].dts[0].dt == in_timezone(exception_date, "Europe/Vienna")
assert exdate[i].to_ical() == exception_date_ics
@pytest.mark.parametrize('freq, byday, dtstart, expected', [
# Test some YEARLY BYDAY repeats
('YEARLY', '1SU', date(2016,1,3), # 1st Sunday in year
b'BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 1SU\r\nDTSTART;VALUE=DATE:20160103\r\nRRULE:FREQ=YEARLY;BYDAY=1SU\r\nEND:VEVENT\r\n'),
('YEARLY', '53MO', date(1984,12,31), # 53rd Monday in (leap) year
b'BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 53MO\r\nDTSTART;VALUE=DATE:19841231\r\nRRULE:FREQ=YEARLY;BYDAY=53MO\r\nEND:VEVENT\r\n'),
('YEARLY', '-1TU', date(1999,12,28), # Last Tuesday in year
b'BEGIN:VEVENT\r\nSUMMARY:Event YEARLY -1TU\r\nDTSTART;VALUE=DATE:19991228\r\nRRULE:FREQ=YEARLY;BYDAY=-1TU\r\nEND:VEVENT\r\n'),
('YEARLY', '-17WE', date(2000,9,6), # 17th-to-last Wednesday in year
b'BEGIN:VEVENT\r\nSUMMARY:Event YEARLY -17WE\r\nDTSTART;VALUE=DATE:20000906\r\nRRULE:FREQ=YEARLY;BYDAY=-17WE\r\nEND:VEVENT\r\n'),
# Test some MONTHLY BYDAY repeats
('MONTHLY', '2TH', date(2003,4,10), # 2nd Thursday in month
b'BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY 2TH\r\nDTSTART;VALUE=DATE:20030410\r\nRRULE:FREQ=MONTHLY;BYDAY=2TH\r\nEND:VEVENT\r\n'),
('MONTHLY', '-3FR', date(2017,5,12), # 3rd-to-last Friday in month
b'BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY -3FR\r\nDTSTART;VALUE=DATE:20170512\r\nRRULE:FREQ=MONTHLY;BYDAY=-3FR\r\nEND:VEVENT\r\n'),
('MONTHLY', '-5SA', date(2053,11,1), # 5th-to-last Saturday in month
b'BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY -5SA\r\nDTSTART;VALUE=DATE:20531101\r\nRRULE:FREQ=MONTHLY;BYDAY=-5SA\r\nEND:VEVENT\r\n'),
# Specifically test examples from the report of Issue #518
# https://github.com/collective/icalendar/issues/518
('YEARLY', '9MO', date(2023,2,27), # 9th Monday in year
b'BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 9MO\r\nDTSTART;VALUE=DATE:20230227\r\nRRULE:FREQ=YEARLY;BYDAY=9MO\r\nEND:VEVENT\r\n'),
('YEARLY', '10MO', date(2023,3,6), # 10th Monday in year
b'BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 10MO\r\nDTSTART;VALUE=DATE:20230306\r\nRRULE:FREQ=YEARLY;BYDAY=10MO\r\nEND:VEVENT\r\n'),
])
@pytest.mark.parametrize(
("freq", "byday", "dtstart", "expected"),
[
# Test some YEARLY BYDAY repeats
(
"YEARLY",
"1SU",
date(2016, 1, 3), # 1st Sunday in year
b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 1SU\r\nDTSTART;VALUE=DATE:20160103\r\nRRULE:FREQ=YEARLY;BYDAY=1SU\r\nEND:VEVENT\r\n",
),
(
"YEARLY",
"53MO",
date(1984, 12, 31), # 53rd Monday in (leap) year
b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 53MO\r\nDTSTART;VALUE=DATE:19841231\r\nRRULE:FREQ=YEARLY;BYDAY=53MO\r\nEND:VEVENT\r\n",
),
(
"YEARLY",
"-1TU",
date(1999, 12, 28), # Last Tuesday in year
b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY -1TU\r\nDTSTART;VALUE=DATE:19991228\r\nRRULE:FREQ=YEARLY;BYDAY=-1TU\r\nEND:VEVENT\r\n",
),
(
"YEARLY",
"-17WE",
date(2000, 9, 6), # 17th-to-last Wednesday in year
b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY -17WE\r\nDTSTART;VALUE=DATE:20000906\r\nRRULE:FREQ=YEARLY;BYDAY=-17WE\r\nEND:VEVENT\r\n",
),
# Test some MONTHLY BYDAY repeats
(
"MONTHLY",
"2TH",
date(2003, 4, 10), # 2nd Thursday in month
b"BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY 2TH\r\nDTSTART;VALUE=DATE:20030410\r\nRRULE:FREQ=MONTHLY;BYDAY=2TH\r\nEND:VEVENT\r\n",
),
(
"MONTHLY",
"-3FR",
date(2017, 5, 12), # 3rd-to-last Friday in month
b"BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY -3FR\r\nDTSTART;VALUE=DATE:20170512\r\nRRULE:FREQ=MONTHLY;BYDAY=-3FR\r\nEND:VEVENT\r\n",
),
(
"MONTHLY",
"-5SA",
date(2053, 11, 1), # 5th-to-last Saturday in month
b"BEGIN:VEVENT\r\nSUMMARY:Event MONTHLY -5SA\r\nDTSTART;VALUE=DATE:20531101\r\nRRULE:FREQ=MONTHLY;BYDAY=-5SA\r\nEND:VEVENT\r\n",
),
# Specifically test examples from the report of Issue #518
# https://github.com/collective/icalendar/issues/518
(
"YEARLY",
"9MO",
date(2023, 2, 27), # 9th Monday in year
b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 9MO\r\nDTSTART;VALUE=DATE:20230227\r\nRRULE:FREQ=YEARLY;BYDAY=9MO\r\nEND:VEVENT\r\n",
),
(
"YEARLY",
"10MO",
date(2023, 3, 6), # 10th Monday in year
b"BEGIN:VEVENT\r\nSUMMARY:Event YEARLY 10MO\r\nDTSTART;VALUE=DATE:20230306\r\nRRULE:FREQ=YEARLY;BYDAY=10MO\r\nEND:VEVENT\r\n",
),
],
)
def test_byday_to_ical(freq, byday, dtstart, expected):
'Test the BYDAY rule is correctly processed by to_ical().'
"""Test the BYDAY rule is correctly processed by to_ical()."""
event = Event()
event.add('SUMMARY', ' '.join(['Event', freq, byday]))
event.add('DTSTART', dtstart)
event.add('RRULE', {'FREQ':[freq], 'BYDAY':byday})
event.add("SUMMARY", " ".join(["Event", freq, byday]))
event.add("DTSTART", dtstart)
event.add("RRULE", {"FREQ": [freq], "BYDAY": byday})
assert event.to_ical() == expected

Wyświetl plik

@ -4,18 +4,20 @@ See
- https://github.com/collective/icalendar/issues/653
- https://www.rfc-editor.org/rfc/rfc7529.html
"""
import pytest
from icalendar.prop import vRecur, vMonth, vSkip
from icalendar.prop import vMonth, vRecur, vSkip
@pytest.mark.parametrize(
"uid,scale",
("uid", "scale"),
[
("4.3.1", "CHINESE"),
("4.3.2", "ETHIOPIC"),
("4.3.3", "HEBREW"),
("4.3.4", "GREGORIAN"),
]
],
)
def test_rscale(calendars, uid, scale):
"""Check that the RSCALE is parsed correctly."""
@ -27,13 +29,13 @@ def test_rscale(calendars, uid, scale):
@pytest.mark.parametrize(
"uid,skip",
("uid", "skip"),
[
("4.3.2", None),
("4.3.3", ["FORWARD"]),
]
],
)
def test_rscale(calendars, uid, skip):
def test_rscale_with_skip(calendars, uid, skip):
"""Check that the RSCALE is parsed correctly."""
event = calendars.rfc_7529.walk(select=lambda c: c.get("UID") == uid)[0]
recur = event["RRULE"]
@ -48,9 +50,13 @@ def test_leap_month(calendars):
@pytest.mark.parametrize(
"ty, recur, ics",
("ty", "recur", "ics"),
[
(vRecur, vRecur(rscale="CHINESE", freq="YEARLY"), b"RSCALE=CHINESE;FREQ=YEARLY"),
(
vRecur,
vRecur(rscale="CHINESE", freq="YEARLY"),
b"RSCALE=CHINESE;FREQ=YEARLY",
),
(vRecur, vRecur(bymonth=vMonth(10)), b"BYMONTH=10"),
(vRecur, vRecur(bymonth=vMonth("5L")), b"BYMONTH=5L"),
(vMonth, vMonth(10), b"10"),
@ -61,9 +67,17 @@ def test_leap_month(calendars):
(vSkip, vSkip("OMIT"), b"OMIT"),
(vSkip, vSkip("BACKWARD"), b"BACKWARD"),
(vSkip, vSkip("FORWARD"), b"FORWARD"),
(vRecur, vRecur(rscale="GREGORIAN", freq="YEARLY", skip='FORWARD'), b"RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD"),
(vRecur, vRecur(rscale="GREGORIAN", freq="YEARLY", skip=vSkip.FORWARD), b"RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD"),
]
(
vRecur,
vRecur(rscale="GREGORIAN", freq="YEARLY", skip="FORWARD"),
b"RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD",
),
(
vRecur,
vRecur(rscale="GREGORIAN", freq="YEARLY", skip=vSkip.FORWARD),
b"RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD",
),
],
)
def test_conversion(ty, recur, ics):
"""Test string conversion."""

Wyświetl plik

@ -1,28 +1,30 @@
import datetime
import icalendar
import os
import icalendar
def test_value_type_is_not_mapped():
"""Usually, the value should be absent."""
assert 'X-SOMETIME' not in icalendar.cal.types_factory.types_map
assert "X-SOMETIME" not in icalendar.cal.types_factory.types_map
def test_value_type_is_mapped(x_sometime):
"""The value is mapped for the test."""
assert 'X-SOMETIME' in icalendar.cal.types_factory.types_map
assert "X-SOMETIME" in icalendar.cal.types_factory.types_map
def test_create_from_ical(x_sometime):
directory = os.path.dirname(__file__)
ics = open(os.path.join(directory, 'calendars', 'time.ics'), 'rb')
ics = open(os.path.join(directory, "calendars", "time.ics"), "rb")
cal = icalendar.Calendar.from_ical(ics.read())
ics.close()
assert cal['X-SOMETIME'].dt == datetime.time(17, 20, 10)
assert cal['X-SOMETIME'].to_ical() == '172010'
assert cal["X-SOMETIME"].dt == datetime.time(17, 20, 10)
assert cal["X-SOMETIME"].to_ical() == "172010"
def test_create_to_ical(x_sometime):
cal = icalendar.Calendar()
cal.add('X-SOMETIME', datetime.time(17, 20, 10))
assert b'X-SOMETIME;VALUE=TIME:172010' in cal.to_ical().splitlines()
cal.add("X-SOMETIME", datetime.time(17, 20, 10))
assert b"X-SOMETIME;VALUE=TIME:172010" in cal.to_ical().splitlines()

Wyświetl plik

@ -1,6 +1,7 @@
import datetime
import dateutil.parser
import icalendar
from icalendar.prop import tzid_from_dt
@ -9,49 +10,53 @@ def test_create_from_ical(calendars, other_tzp):
"""Create a calendar from a .ics file."""
cal = calendars.timezoned
assert cal['prodid'].to_ical() == b"-//Plone.org//NONSGML plone.app.event//EN"
assert cal["prodid"].to_ical() == b"-//Plone.org//NONSGML plone.app.event//EN"
timezones = cal.walk('VTIMEZONE')
timezones = cal.walk("VTIMEZONE")
assert len(timezones) == 1
tz = timezones[0]
assert tz['tzid'].to_ical() == b"Europe/Vienna"
assert tz["tzid"].to_ical() == b"Europe/Vienna"
std = tz.walk('STANDARD')[0]
assert std.decoded('TZOFFSETFROM') == datetime.timedelta(0, 7200)
std = tz.walk("STANDARD")[0]
assert std.decoded("TZOFFSETFROM") == datetime.timedelta(0, 7200)
ev1 = cal.walk('VEVENT')[0]
assert ev1.decoded('DTSTART') == other_tzp.localize(datetime.datetime(2012, 2, 13, 10, 0, 0), 'Europe/Vienna')
assert ev1.decoded('DTSTAMP') == other_tzp.localize(datetime.datetime(2010, 10, 10, 9, 10, 10), 'UTC')
ev1 = cal.walk("VEVENT")[0]
assert ev1.decoded("DTSTART") == other_tzp.localize(
datetime.datetime(2012, 2, 13, 10, 0, 0), "Europe/Vienna"
)
assert ev1.decoded("DTSTAMP") == other_tzp.localize(
datetime.datetime(2010, 10, 10, 9, 10, 10), "UTC"
)
def test_create_to_ical(tzp):
cal = icalendar.Calendar()
cal.add('prodid', "-//Plone.org//NONSGML plone.app.event//EN")
cal.add('version', "2.0")
cal.add('x-wr-calname', "test create calendar")
cal.add('x-wr-caldesc', "icalendar tests")
cal.add('x-wr-relcalid', "12345")
cal.add('x-wr-timezone', "Europe/Vienna")
cal.add("prodid", "-//Plone.org//NONSGML plone.app.event//EN")
cal.add("version", "2.0")
cal.add("x-wr-calname", "test create calendar")
cal.add("x-wr-caldesc", "icalendar tests")
cal.add("x-wr-relcalid", "12345")
cal.add("x-wr-timezone", "Europe/Vienna")
tzc = icalendar.Timezone()
tzc.add('tzid', 'Europe/Vienna')
tzc.add('x-lic-location', 'Europe/Vienna')
tzc.add("tzid", "Europe/Vienna")
tzc.add("x-lic-location", "Europe/Vienna")
tzs = icalendar.TimezoneStandard()
tzs.add('tzname', 'CET')
tzs.add('dtstart', datetime.datetime(1970, 10, 25, 3, 0, 0))
tzs.add('rrule', {'freq': 'yearly', 'bymonth': 10, 'byday': '-1su'})
tzs.add('TZOFFSETFROM', datetime.timedelta(hours=2))
tzs.add('TZOFFSETTO', datetime.timedelta(hours=1))
tzs.add("tzname", "CET")
tzs.add("dtstart", datetime.datetime(1970, 10, 25, 3, 0, 0))
tzs.add("rrule", {"freq": "yearly", "bymonth": 10, "byday": "-1su"})
tzs.add("TZOFFSETFROM", datetime.timedelta(hours=2))
tzs.add("TZOFFSETTO", datetime.timedelta(hours=1))
tzd = icalendar.TimezoneDaylight()
tzd.add('tzname', 'CEST')
tzd.add('dtstart', datetime.datetime(1970, 3, 29, 2, 0, 0))
tzs.add('rrule', {'freq': 'yearly', 'bymonth': 3, 'byday': '-1su'})
tzd.add('TZOFFSETFROM', datetime.timedelta(hours=1))
tzd.add('TZOFFSETTO', datetime.timedelta(hours=2))
tzd.add("tzname", "CEST")
tzd.add("dtstart", datetime.datetime(1970, 3, 29, 2, 0, 0))
tzs.add("rrule", {"freq": "yearly", "bymonth": 3, "byday": "-1su"})
tzd.add("TZOFFSETFROM", datetime.timedelta(hours=1))
tzd.add("TZOFFSETTO", datetime.timedelta(hours=2))
tzc.add_component(tzs)
tzc.add_component(tzd)
@ -59,36 +64,41 @@ def test_create_to_ical(tzp):
event = icalendar.Event()
event.add(
'dtstart',
tzp.localize(datetime.datetime(2012, 2, 13, 10, 00, 00), "Europe/Vienna"))
"dtstart",
tzp.localize(datetime.datetime(2012, 2, 13, 10, 00, 00), "Europe/Vienna"),
)
event.add(
'dtend',
tzp.localize(datetime.datetime(2012, 2, 17, 18, 00, 00), "Europe/Vienna"))
"dtend",
tzp.localize(datetime.datetime(2012, 2, 17, 18, 00, 00), "Europe/Vienna"),
)
event.add(
'dtstamp',
tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"))
"dtstamp",
tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"),
)
event.add(
'created',
tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"))
event.add('uid', '123456')
"created",
tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"),
)
event.add("uid", "123456")
event.add(
'last-modified',
tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"))
event.add('summary', 'artsprint 2012')
"last-modified",
tzp.localize(datetime.datetime(2010, 10, 10, 10, 10, 10), "Europe/Vienna"),
)
event.add("summary", "artsprint 2012")
# event.add('rrule', 'FREQ=YEARLY;INTERVAL=1;COUNT=10')
event.add('description', 'sprinting at the artsprint')
event.add('location', 'aka bild, wien')
event.add('categories', 'first subject')
event.add('categories', 'second subject')
event.add('attendee', 'häns')
event.add('attendee', 'franz')
event.add('attendee', 'sepp')
event.add('contact', 'Max Mustermann, 1010 Wien')
event.add('url', 'https://plone.org')
event.add("description", "sprinting at the artsprint")
event.add("location", "aka bild, wien")
event.add("categories", "first subject")
event.add("categories", "second subject")
event.add("attendee", "häns")
event.add("attendee", "franz")
event.add("attendee", "sepp")
event.add("contact", "Max Mustermann, 1010 Wien")
event.add("url", "https://plone.org")
cal.add_component(event)
test_out = b'|'.join(cal.to_ical().splitlines())
test_out = test_out.decode('utf-8')
test_out = b"|".join(cal.to_ical().splitlines())
test_out = test_out.decode("utf-8")
vtimezone_lines = "BEGIN:VTIMEZONE|TZID:Europe/Vienna|X-LIC-LOCATION:"
"Europe/Vienna|BEGIN:STANDARD|DTSTART:19701025T03"
@ -100,61 +110,67 @@ def test_create_to_ical(tzp):
assert vtimezone_lines in test_out
test_str = "DTSTART;TZID=Europe/Vienna:20120213T100000"
assert (test_str in test_out)
assert ("ATTENDEE:sepp" in test_out)
assert test_str in test_out
assert "ATTENDEE:sepp" in test_out
# ical standard expects DTSTAMP and CREATED in UTC
assert ("DTSTAMP:20101010T081010Z" in test_out)
assert ("CREATED:20101010T081010Z" in test_out)
assert "DTSTAMP:20101010T081010Z" in test_out
assert "CREATED:20101010T081010Z" in test_out
def test_tzinfo_dateutil():
"""Test for issues #77, #63
references: #73,7430b66862346fe3a6a100ab25e35a8711446717
"""
date = dateutil.parser.parse('2012-08-30T22:41:00Z')
date2 = dateutil.parser.parse('2012-08-30T22:41:00 +02:00')
assert (date.tzinfo.__module__.startswith('dateutil.tz'))
assert (date2.tzinfo.__module__.startswith('dateutil.tz'))
date = dateutil.parser.parse("2012-08-30T22:41:00Z")
date2 = dateutil.parser.parse("2012-08-30T22:41:00 +02:00")
assert date.tzinfo.__module__.startswith("dateutil.tz")
assert date2.tzinfo.__module__.startswith("dateutil.tz")
# make sure, it's parsed properly and doesn't throw an error
assert (icalendar.vDDDTypes(date).to_ical()
== b'20120830T224100Z')
assert (icalendar.vDDDTypes(date2).to_ical()
== b'20120830T224100')
assert icalendar.vDDDTypes(date).to_ical() == b"20120830T224100Z"
assert icalendar.vDDDTypes(date2).to_ical() == b"20120830T224100"
def test_create_america_new_york(calendars, tzp):
"""testing America/New_York, the most complex example from the RFC"""
cal = calendars.america_new_york
dt = cal.walk('VEVENT')[0]['DTSTART'][0].dt
assert tzid_from_dt(dt) in ('custom_America/New_York', "EDT")
dt = cal.walk("VEVENT")[0]["DTSTART"][0].dt
assert tzid_from_dt(dt) in ("custom_America/New_York", "EDT")
def test_america_new_york_with_pytz(calendars, tzp, pytz_only):
"""Create a custom timezone with pytz and test the transition times."""
print(tzp)
cal = calendars.america_new_york
dt = cal.walk('VEVENT')[0]['DTSTART'][0].dt
dt = cal.walk("VEVENT")[0]["DTSTART"][0].dt
tz = dt.tzinfo
tz_new_york = tzp.timezone('America/New_York')
tz_new_york = tzp.timezone("America/New_York")
# for reasons (tm) the locally installed version of the timezone
# database isn't always complete, therefore we only compare some
# transition times
ny_transition_times = []
ny_transition_info = []
for num, date in enumerate(tz_new_york._utc_transition_times):
if datetime.datetime(1967, 4, 30, 7, 0)\
<= date <= datetime.datetime(2037, 11, 1, 6, 0):
if (
datetime.datetime(1967, 4, 30, 7, 0)
<= date
<= datetime.datetime(2037, 11, 1, 6, 0)
):
ny_transition_times.append(date)
ny_transition_info.append(tz_new_york._transition_info[num])
assert tz._utc_transition_times[:142] == ny_transition_times
assert tz._transition_info[0:142] == ny_transition_info
assert (
datetime.timedelta(-1, 72000),
datetime.timedelta(0, 3600), 'EDT'
) in tz._tzinfos.keys()
assert (datetime.timedelta(-1, 68400), datetime.timedelta(0), 'EST') in tz._tzinfos.keys()
datetime.timedelta(-1, 72000),
datetime.timedelta(0, 3600),
"EDT",
) in tz._tzinfos.keys()
assert (
datetime.timedelta(-1, 68400),
datetime.timedelta(0),
"EST",
) in tz._tzinfos.keys()
fiji_transition_times = [
@ -221,68 +237,85 @@ fiji_transition_times = [
datetime.datetime(2037, 1, 17, 13, 0),
datetime.datetime(2037, 10, 24, 14, 0),
datetime.datetime(2038, 1, 23, 13, 0),
datetime.datetime(2038, 10, 23, 14, 0)
datetime.datetime(2038, 10, 23, 14, 0),
]
fiji_transition_info = (
[(
datetime.timedelta(0, 43200),
datetime.timedelta(0),
'custom_Pacific/Fiji_19151026T000000_+115544_+1200'
)] +
3 * [(
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
'custom_Pacific/Fiji_19981101T020000_+1200_+1300'
), (
datetime.timedelta(0, 43200),
datetime.timedelta(0),
'custom_Pacific/Fiji_19990228T030000_+1300_+1200')
] +
3 * [(
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
'custom_Pacific/Fiji_20101024T020000_+1200_+1300'
), (
datetime.timedelta(0, 43200),
datetime.timedelta(0),
'custom_Pacific/Fiji_19990228T030000_+1300_+1200'
)] +
25 * [(
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
'custom_Pacific/Fiji_20101024T020000_+1200_+1300'
), (
datetime.timedelta(0, 43200),
datetime.timedelta(0),
'custom_Pacific/Fiji_20140119T020000_+1300_+1200'
)] +
[(
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
'custom_Pacific/Fiji_20101024T020000_+1200_+1300'
)]
[
(
datetime.timedelta(0, 43200),
datetime.timedelta(0),
"custom_Pacific/Fiji_19151026T000000_+115544_+1200",
)
]
+ 3
* [
(
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
"custom_Pacific/Fiji_19981101T020000_+1200_+1300",
),
(
datetime.timedelta(0, 43200),
datetime.timedelta(0),
"custom_Pacific/Fiji_19990228T030000_+1300_+1200",
),
]
+ 3
* [
(
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
"custom_Pacific/Fiji_20101024T020000_+1200_+1300",
),
(
datetime.timedelta(0, 43200),
datetime.timedelta(0),
"custom_Pacific/Fiji_19990228T030000_+1300_+1200",
),
]
+ 25
* [
(
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
"custom_Pacific/Fiji_20101024T020000_+1200_+1300",
),
(
datetime.timedelta(0, 43200),
datetime.timedelta(0),
"custom_Pacific/Fiji_20140119T020000_+1300_+1200",
),
]
+ [
(
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
"custom_Pacific/Fiji_20101024T020000_+1200_+1300",
)
]
)
def test_create_pacific_fiji(calendars, pytz_only):
"""testing Pacific/Fiji, another pretty complex example with more than
one RDATE property per subcomponent"""
cal = calendars.pacific_fiji
tz = cal.walk('VEVENT')[0]['DTSTART'][0].dt.tzinfo
assert str(tz) == 'custom_Pacific/Fiji'
tz = cal.walk("VEVENT")[0]["DTSTART"][0].dt.tzinfo
assert str(tz) == "custom_Pacific/Fiji"
assert tz._utc_transition_times == fiji_transition_times
assert tz._transition_info == fiji_transition_info
assert (
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
'custom_Pacific/Fiji_19981101T020000_+1200_+1300'
) in tz._tzinfos.keys()
datetime.timedelta(0, 46800),
datetime.timedelta(0, 3600),
"custom_Pacific/Fiji_19981101T020000_+1200_+1300",
) in tz._tzinfos.keys()
assert (
datetime.timedelta(0, 43200),
datetime.timedelta(0),
'custom_Pacific/Fiji_19990228T030000_+1300_+1200'
) in tz._tzinfos.keys()
datetime.timedelta(0, 43200),
datetime.timedelta(0),
"custom_Pacific/Fiji_19990228T030000_+1300_+1200",
) in tz._tzinfos.keys()
# these are the expected offsets before and after the fiji_transition_times
@ -357,7 +390,7 @@ fiji_expected_offsets = [
def test_transition_times_fiji(tzp, timezones):
"""The transition times are computed."""
tz = timezones.pacific_fiji.to_tz(tzp)
offsets = [] # [(before, after), ...]
offsets = [] # [(before, after), ...]
for i, transition_time in enumerate(fiji_transition_times):
before_after_offset = []
for offset in (datetime.timedelta(hours=-1), datetime.timedelta(hours=+1)):
@ -372,42 +405,43 @@ def test_same_start_date(calendars):
"""testing if we can handle VTIMEZONEs whose different components
have the same start DTIMEs."""
cal = calendars.timezone_same_start
d = cal.subcomponents[1]['DTSTART'].dt
assert d.strftime('%c') == 'Fri Feb 24 12:00:00 2017'
d = cal.subcomponents[1]["DTSTART"].dt
assert d.strftime("%c") == "Fri Feb 24 12:00:00 2017"
def test_same_start_date_and_offset(calendars):
"""testing if we can handle VTIMEZONEs whose different components
have the same DTSTARTs, TZOFFSETFROM, and TZOFFSETTO."""
cal = calendars.timezone_same_start_and_offset
d = cal.subcomponents[1]['DTSTART'].dt
assert d.strftime('%c') == 'Fri Feb 24 12:00:00 2017'
d = cal.subcomponents[1]["DTSTART"].dt
assert d.strftime("%c") == "Fri Feb 24 12:00:00 2017"
def test_rdate(calendars):
"""testing if we can handle VTIMEZONEs who only have an RDATE, not RRULE
"""
"""testing if we can handle VTIMEZONEs who only have an RDATE, not RRULE"""
cal = calendars.timezone_rdate
vevent = cal.walk('VEVENT')[0]
assert tzid_from_dt(vevent['DTSTART'].dt) in ('posix/Europe/Vaduz', "CET")
vevent = cal.walk("VEVENT")[0]
assert tzid_from_dt(vevent["DTSTART"].dt) in ("posix/Europe/Vaduz", "CET")
def test_rdate_pytz(calendars, pytz_only):
"""testing if we can handle VTIMEZONEs who only have an RDATE, not RRULE
"""
"""testing if we can handle VTIMEZONEs who only have an RDATE, not RRULE"""
cal = calendars.timezone_rdate
vevent = cal.walk('VEVENT')[0]
tz = vevent['DTSTART'].dt.tzinfo
vevent = cal.walk("VEVENT")[0]
tz = vevent["DTSTART"].dt.tzinfo
assert tz._utc_transition_times[:6] == [
datetime.datetime(1901, 12, 13, 20, 45, 38),
datetime.datetime(1941, 5, 5, 0, 0, 0),
datetime.datetime(1941, 10, 6, 0, 0, 0),
datetime.datetime(1942, 5, 4, 0, 0, 0),
datetime.datetime(1942, 10, 5, 0, 0, 0),
datetime.datetime(1981, 3, 29, 1, 0),
]
datetime.datetime(1901, 12, 13, 20, 45, 38),
datetime.datetime(1941, 5, 5, 0, 0, 0),
datetime.datetime(1941, 10, 6, 0, 0, 0),
datetime.datetime(1942, 5, 4, 0, 0, 0),
datetime.datetime(1942, 10, 5, 0, 0, 0),
datetime.datetime(1981, 3, 29, 1, 0),
]
assert tz._transition_info[:6] == [
(datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET'),
(datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST'),
(datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET'),
(datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST'),
(datetime.timedelta(0, 3600), datetime.timedelta(0), 'CET'),
(datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), 'CEST'),
]
(datetime.timedelta(0, 3600), datetime.timedelta(0), "CET"),
(datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), "CEST"),
(datetime.timedelta(0, 3600), datetime.timedelta(0), "CET"),
(datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), "CEST"),
(datetime.timedelta(0, 3600), datetime.timedelta(0), "CET"),
(datetime.timedelta(0, 7200), datetime.timedelta(0, 3600), "CEST"),
]

Wyświetl plik

@ -1,13 +1,12 @@
import itertools
from datetime import datetime
from datetime import timedelta
import re
from datetime import datetime, timedelta
import pytest
import icalendar
import re
from icalendar.cal import Component, Calendar, Event
from icalendar import prop
from icalendar.cal import Calendar, Component, Event
from icalendar.prop import tzid_from_dt
@ -21,15 +20,15 @@ def test_nonempty_calendar_component(calendar_component):
"""Every key defines a property.A property can consist of either a
single item. This can be set with a single value...
"""
calendar_component['prodid'] = '-//max m//icalendar.mxm.dk/'
calendar_component["prodid"] = "-//max m//icalendar.mxm.dk/"
assert not calendar_component.is_empty()
assert calendar_component == Calendar({'PRODID': '-//max m//icalendar.mxm.dk/'})
assert calendar_component == Calendar({"PRODID": "-//max m//icalendar.mxm.dk/"})
# or with a list
calendar_component['ATTENDEE'] = ['Max M', 'Rasmussen']
calendar_component["ATTENDEE"] = ["Max M", "Rasmussen"]
assert calendar_component == Calendar(
{'ATTENDEE': ['Max M', 'Rasmussen'],
'PRODID': '-//max m//icalendar.mxm.dk/'})
{"ATTENDEE": ["Max M", "Rasmussen"], "PRODID": "-//max m//icalendar.mxm.dk/"}
)
def test_add_multiple_values(event_component):
@ -39,57 +38,62 @@ def test_add_multiple_values(event_component):
a list or not.
"""
# add multiple values at once
event_component.add('attendee',
['test@test.com', 'test2@test.com'])
event_component.add("attendee", ["test@test.com", "test2@test.com"])
# or add one per line
event_component.add('attendee', 'maxm@mxm.dk')
event_component.add('attendee', 'test@example.dk')
event_component.add("attendee", "maxm@mxm.dk")
event_component.add("attendee", "test@example.dk")
# add again multiple values at once to very concatenaton of lists
event_component.add('attendee',
['test3@test.com', 'test4@test.com'])
event_component.add("attendee", ["test3@test.com", "test4@test.com"])
assert event_component == Event({'ATTENDEE': [
prop.vCalAddress('test@test.com'),
prop.vCalAddress('test2@test.com'),
prop.vCalAddress('maxm@mxm.dk'),
prop.vCalAddress('test@example.dk'),
prop.vCalAddress('test3@test.com'),
prop.vCalAddress('test4@test.com')
]})
assert event_component == Event(
{
"ATTENDEE": [
prop.vCalAddress("test@test.com"),
prop.vCalAddress("test2@test.com"),
prop.vCalAddress("maxm@mxm.dk"),
prop.vCalAddress("test@example.dk"),
prop.vCalAddress("test3@test.com"),
prop.vCalAddress("test4@test.com"),
]
}
)
def test_get_content_directly(c):
"""You can get the values back directly ..."""
c.add('prodid', '-//my product//')
assert c['prodid'] == prop.vText('-//my product//')
c.add("prodid", "-//my product//")
assert c["prodid"] == prop.vText("-//my product//")
# ... or decoded to a python type
assert c.decoded('prodid') == b'-//my product//'
assert c.decoded("prodid") == b"-//my product//"
def test_get_default_value(c):
"""With default values for non existing properties"""
assert c.decoded('version', 'No Version') == 'No Version'
assert c.decoded("version", "No Version") == "No Version"
def test_default_list_example(c):
c.add('rdate', [datetime(2013, 3, 28), datetime(2013, 3, 27)])
assert isinstance(c.decoded('rdate'), prop.vDDDLists)
c.add("rdate", [datetime(2013, 3, 28), datetime(2013, 3, 27)])
assert isinstance(c.decoded("rdate"), prop.vDDDLists)
def test_render_component(calendar_component):
"""The component can render itself in the RFC 5545 format."""
calendar_component.add('attendee', 'Max M')
assert calendar_component.to_ical() == b'BEGIN:VCALENDAR\r\nATTENDEE:Max M\r\nEND:VCALENDAR\r\n'
calendar_component.add("attendee", "Max M")
assert (
calendar_component.to_ical()
== b"BEGIN:VCALENDAR\r\nATTENDEE:Max M\r\nEND:VCALENDAR\r\n"
)
def test_nested_component_event_ics(filled_event_component):
"""Check the ical string of the event component."""
assert filled_event_component.to_ical() == (
b'BEGIN:VEVENT\r\nDTEND:20000102T000000\r\n'
+ b'DTSTART:20000101T000000\r\nSUMMARY:A brief history of time\r'
+ b'\nEND:VEVENT\r\n'
b"BEGIN:VEVENT\r\nDTEND:20000102T000000\r\n"
+ b"DTSTART:20000101T000000\r\nSUMMARY:A brief history of time\r"
+ b"\nEND:VEVENT\r\n"
)
@ -98,58 +102,75 @@ def test_nested_components(calendar_component, filled_event_component):
holds events."""
self.assertEqual(
calendar_component.subcomponents,
[Event({'DTEND': '20000102T000000', 'DTSTART': '20000101T000000',
'SUMMARY': 'A brief history of time'})]
[
Event(
{
"DTEND": "20000102T000000",
"DTSTART": "20000101T000000",
"SUMMARY": "A brief history of time",
}
)
],
)
def test_walk_filled_calendar_component(calendar_component, filled_event_component):
"""We can walk over nested componentes with the walk method."""
assert [i.name for i in calendar_component.walk()] == ['VCALENDAR', 'VEVENT']
assert [i.name for i in calendar_component.walk()] == ["VCALENDAR", "VEVENT"]
def test_filter_walk(calendar_component, filled_event_component):
"""We can also just walk over specific component types, by filtering
them on their name."""
assert [i.name for i in calendar_component.walk('VEVENT')] == ['VEVENT']
assert [i['dtstart'] for i in calendar_component.walk('VEVENT')] == ['20000101T000000']
assert [i.name for i in calendar_component.walk("VEVENT")] == ["VEVENT"]
assert [i["dtstart"] for i in calendar_component.walk("VEVENT")] == [
"20000101T000000"
]
def test_recursive_property_items(calendar_component, filled_event_component):
"""We can enumerate property items recursively with the property_items
method."""
calendar_component.add('attendee', 'Max M')
calendar_component.add("attendee", "Max M")
assert calendar_component.property_items() == [
('BEGIN', b'VCALENDAR'), ('ATTENDEE', prop.vCalAddress('Max M')),
('BEGIN', b'VEVENT'), ('DTEND', '20000102T000000'),
('DTSTART', '20000101T000000'),
('SUMMARY', 'A brief history of time'), ('END', b'VEVENT'),
('END', b'VCALENDAR')]
("BEGIN", b"VCALENDAR"),
("ATTENDEE", prop.vCalAddress("Max M")),
("BEGIN", b"VEVENT"),
("DTEND", "20000102T000000"),
("DTSTART", "20000101T000000"),
("SUMMARY", "A brief history of time"),
("END", b"VEVENT"),
("END", b"VCALENDAR"),
]
def test_flat_property_items(calendar_component, filled_event_component):
"""We can also enumerate property items just under the component."""
assert calendar_component.property_items(recursive=False) == [
('BEGIN', b'VCALENDAR'),
('ATTENDEE', prop.vCalAddress('Max M')),
('END', b'VCALENDAR')]
("BEGIN", b"VCALENDAR"),
("ATTENDEE", prop.vCalAddress("Max M")),
("END", b"VCALENDAR"),
]
def test_flat_property_items(filled_event_component):
"""Flat enumeration on the event."""
assert filled_event_component.property_items(recursive=False) == [
('BEGIN', b'VEVENT'), ('DTEND', '20000102T000000'),
('DTSTART', '20000101T000000'),
('SUMMARY', 'A brief history of time'), ('END', b'VEVENT')]
("BEGIN", b"VEVENT"),
("DTEND", "20000102T000000"),
("DTSTART", "20000101T000000"),
("SUMMARY", "A brief history of time"),
("END", b"VEVENT"),
]
def test_indent():
"""Text fields which span multiple mulitple lines require proper indenting"""
c = Calendar()
c['description'] = 'Paragraph one\n\nParagraph two'
c["description"] = "Paragraph one\n\nParagraph two"
assert c.to_ical() == (
b'BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\\n\\nParagraph two'
+ b'\r\nEND:VCALENDAR\r\n'
b"BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\\n\\nParagraph two"
+ b"\r\nEND:VCALENDAR\r\n"
)
@ -157,10 +178,12 @@ def test_INLINE_properties(calendar_with_resources):
"""INLINE properties have their values on one property line. Note the
double quoting of the value with a colon in it.
"""
assert calendar_with_resources == Calendar({'RESOURCES': 'Chair, Table, "Room: 42"'})
assert calendar_with_resources == Calendar(
{"RESOURCES": 'Chair, Table, "Room: 42"'}
)
assert calendar_with_resources.to_ical() == (
b'BEGIN:VCALENDAR\r\nRESOURCES:Chair\\, Table\\, "Room: 42"\r\n'
+ b'END:VCALENDAR\r\n'
+ b"END:VCALENDAR\r\n"
)
@ -168,32 +191,48 @@ def test_get_inline(calendar_with_resources):
"""The inline values must be handled by the get_inline() and
set_inline() methods.
"""
assert calendar_with_resources.get_inline('resources', decode=0) == \
['Chair', 'Table', 'Room: 42']
assert calendar_with_resources.get_inline("resources", decode=0) == [
"Chair",
"Table",
"Room: 42",
]
def test_get_inline_decoded(calendar_with_resources):
"""These can also be decoded"""
assert calendar_with_resources.get_inline('resources', decode=1) == \
[b'Chair', b'Table', b'Room: 42']
assert calendar_with_resources.get_inline("resources", decode=1) == [
b"Chair",
b"Table",
b"Room: 42",
]
def test_set_inline(calendar_with_resources):
"""You can set them directly ..."""
calendar_with_resources.set_inline('resources', ['A', 'List', 'of', 'some, recources'],
encode=1)
assert calendar_with_resources['resources'] == 'A,List,of,"some, recources"'
assert calendar_with_resources.get_inline('resources', decode=0) == ['A', 'List', 'of', 'some, recources']
calendar_with_resources.set_inline(
"resources", ["A", "List", "of", "some, recources"], encode=1
)
assert calendar_with_resources["resources"] == 'A,List,of,"some, recources"'
assert calendar_with_resources.get_inline("resources", decode=0) == [
"A",
"List",
"of",
"some, recources",
]
def test_inline_free_busy_inline(c):
c['freebusy'] = '19970308T160000Z/PT3H,19970308T200000Z/PT1H,'\
+ '19970308T230000Z/19970309T000000Z'
assert c.get_inline('freebusy', decode=0) == \
['19970308T160000Z/PT3H', '19970308T200000Z/PT1H',
'19970308T230000Z/19970309T000000Z']
c["freebusy"] = (
"19970308T160000Z/PT3H,19970308T200000Z/PT1H,"
+ "19970308T230000Z/19970309T000000Z"
)
assert c.get_inline("freebusy", decode=0) == [
"19970308T160000Z/PT3H",
"19970308T200000Z/PT1H",
"19970308T230000Z/19970309T000000Z",
]
freebusy = c.get_inline('freebusy', decode=1)
freebusy = c.get_inline("freebusy", decode=1)
assert isinstance(freebusy[0][0], datetime)
assert isinstance(freebusy[0][1], timedelta)
@ -202,11 +241,10 @@ def test_cal_Component_add(comp, tzp):
"""Test the for timezone correctness: dtstart should preserve it's
timezone, created, dtstamp and last-modified must be in UTC.
"""
comp.add('dtstart', tzp.localize(datetime(2010, 10, 10, 10, 0, 0), "Europe/Vienna"))
comp.add('created', datetime(2010, 10, 10, 12, 0, 0))
comp.add('dtstamp', tzp.localize(datetime(2010, 10, 10, 14, 0, 0), "Europe/Vienna"))
comp.add('last-modified', tzp.localize_utc(
datetime(2010, 10, 10, 16, 0, 0)))
comp.add("dtstart", tzp.localize(datetime(2010, 10, 10, 10, 0, 0), "Europe/Vienna"))
comp.add("created", datetime(2010, 10, 10, 12, 0, 0))
comp.add("dtstamp", tzp.localize(datetime(2010, 10, 10, 14, 0, 0), "Europe/Vienna"))
comp.add("last-modified", tzp.localize_utc(datetime(2010, 10, 10, 16, 0, 0)))
lines = comp.to_ical().splitlines()
assert b"DTSTART;TZID=Europe/Vienna:20101010T100000" in lines
@ -216,22 +254,20 @@ def test_cal_Component_add(comp, tzp):
def test_cal_Component_add_no_reencode(comp):
"""Already encoded values should not be re-encoded.
"""
comp.add('ATTACH', 'me')
comp.add('ATTACH', 'you', encode=False)
binary = prop.vBinary('us')
comp.add('ATTACH', binary)
"""Already encoded values should not be re-encoded."""
comp.add("ATTACH", "me")
comp.add("ATTACH", "you", encode=False)
binary = prop.vBinary("us")
comp.add("ATTACH", binary)
assert comp['ATTACH'] == ['me', 'you', binary]
assert comp["ATTACH"] == ["me", "you", binary]
def test_cal_Component_add_property_parameter(comp):
"""Test the for timezone correctness: dtstart should preserve it's
timezone, crated, dtstamp and last-modified must be in UTC.
"""
comp.add('X-TEST-PROP', 'tryout.',
parameters={'prop1': 'val1', 'prop2': 'val2'})
comp.add("X-TEST-PROP", "tryout.", parameters={"prop1": "val1", "prop2": "val2"})
lines = comp.to_ical().splitlines()
assert b"X-TEST-PROP;PROP1=val1;PROP2=val2:tryout." in lines
@ -239,20 +275,20 @@ def test_cal_Component_add_property_parameter(comp):
comp_prop = pytest.mark.parametrize(
"component_name, property_name",
[
('VEVENT', 'DTSTART'),
('VEVENT', 'DTEND'),
('VEVENT', 'RECURRENCE-ID'),
('VTODO', 'DUE')
]
("VEVENT", "DTSTART"),
("VEVENT", "DTEND"),
("VEVENT", "RECURRENCE-ID"),
("VTODO", "DUE"),
],
)
@comp_prop
def test_cal_Component_from_ical(component_name, property_name, tzp):
"""Check for proper handling of TZID parameter of datetime properties"""
component_str = 'BEGIN:' + component_name + '\n'
component_str += property_name + ';TZID=America/Denver:'
component_str += '20120404T073000\nEND:' + component_name
component_str = "BEGIN:" + component_name + "\n"
component_str += property_name + ";TZID=America/Denver:"
component_str += "20120404T073000\nEND:" + component_name
component = Component.from_ical(component_str)
assert tzid_from_dt(component[property_name].dt) == "America/Denver"
@ -260,20 +296,22 @@ def test_cal_Component_from_ical(component_name, property_name, tzp):
@comp_prop
def test_cal_Component_from_ical_2(component_name, property_name, tzp):
"""Check for proper handling of TZID parameter of datetime properties"""
component_str = 'BEGIN:' + component_name + '\n'
component_str += property_name + ':'
component_str += '20120404T073000\nEND:' + component_name
component_str = "BEGIN:" + component_name + "\n"
component_str += property_name + ":"
component_str += "20120404T073000\nEND:" + component_name
component = Component.from_ical(component_str)
assert component[property_name].dt.tzinfo == None
def test_cal_Component_to_ical_property_order():
component_str = [b'BEGIN:VEVENT',
b'DTSTART:19970714T170000Z',
b'DTEND:19970715T035959Z',
b'SUMMARY:Bastille Day Party',
b'END:VEVENT']
component = Component.from_ical(b'\r\n'.join(component_str))
component_str = [
b"BEGIN:VEVENT",
b"DTSTART:19970714T170000Z",
b"DTEND:19970715T035959Z",
b"SUMMARY:Bastille Day Party",
b"END:VEVENT",
]
component = Component.from_ical(b"\r\n".join(component_str))
sorted_str = component.to_ical().splitlines()
assert sorted_str != component_str
@ -284,39 +322,43 @@ def test_cal_Component_to_ical_property_order():
def test_cal_Component_to_ical_parameter_order():
component_str = [b'BEGIN:VEVENT',
b'X-FOOBAR;C=one;A=two;B=three:helloworld.',
b'END:VEVENT']
component = Component.from_ical(b'\r\n'.join(component_str))
component_str = [
b"BEGIN:VEVENT",
b"X-FOOBAR;C=one;A=two;B=three:helloworld.",
b"END:VEVENT",
]
component = Component.from_ical(b"\r\n".join(component_str))
sorted_str = component.to_ical().splitlines()
assert sorted_str[0] == component_str[0]
assert sorted_str[1] == b'X-FOOBAR;A=two;B=three;C=one:helloworld.'
assert sorted_str[1] == b"X-FOOBAR;A=two;B=three;C=one:helloworld."
assert sorted_str[2] == component_str[2]
preserved_str = component.to_ical(sorted=False).splitlines()
assert preserved_str == component_str
@pytest.fixture()
@pytest.fixture
def repr_example(c):
class ReprExample:
component = c
component['key1'] = 'value1'
component["key1"] = "value1"
calendar = Calendar()
calendar['key1'] = 'value1'
calendar["key1"] = "value1"
event = Event()
event['key1'] = 'value1'
nested = Component(key1='VALUE1')
event["key1"] = "value1"
nested = Component(key1="VALUE1")
nested.add_component(component)
nested.add_component(calendar)
return ReprExample
def test_repr_component(repr_example):
"""Test correct class representation.
"""
"""Test correct class representation."""
assert re.match(r"Component\({u?'KEY1': u?'value1'}\)", str(repr_example.component))
def test_repr_calendar(repr_example):
assert re.match(r"VCALENDAR\({u?'KEY1': u?'value1'}\)", str(repr_example.calendar))
@ -330,24 +372,24 @@ def test_nested_components(repr_example):
repr_example.calendar.add_component(repr_example.event)
print(repr_example.nested)
assert re.match(
r"Component\({u?'KEY1': u?'VALUE1'}, "
r"Component\({u?'KEY1': u?'value1'}\), "
r"VCALENDAR\({u?'KEY1': u?'value1'}, "
r"VEVENT\({u?'KEY1': u?'value1'}\)\)\)",
str(repr_example.nested)
)
r"Component\({u?'KEY1': u?'VALUE1'}, "
r"Component\({u?'KEY1': u?'value1'}\), "
r"VCALENDAR\({u?'KEY1': u?'value1'}, "
r"VEVENT\({u?'KEY1': u?'value1'}\)\)\)",
str(repr_example.nested),
)
def test_component_factory_VEVENT(factory):
"""Check the events in the component factory"""
component = factory['VEVENT']
event = component(dtstart='19700101')
assert event.to_ical() == b'BEGIN:VEVENT\r\nDTSTART:19700101\r\nEND:VEVENT\r\n'
component = factory["VEVENT"]
event = component(dtstart="19700101")
assert event.to_ical() == b"BEGIN:VEVENT\r\nDTSTART:19700101\r\nEND:VEVENT\r\n"
def test_component_factory_VCALENDAR(factory):
"""Check the VCALENDAR in the factory."""
assert factory.get('VCALENDAR') == icalendar.cal.Calendar
assert factory.get("VCALENDAR") == icalendar.cal.Calendar
def test_minimal_calendar_component_with_one_event():
@ -355,19 +397,21 @@ def test_minimal_calendar_component_with_one_event():
cal = Calendar()
# Some properties are required to be compliant
cal['prodid'] = '-//My calendar product//mxm.dk//'
cal['version'] = '2.0'
cal["prodid"] = "-//My calendar product//mxm.dk//"
cal["version"] = "2.0"
# We also need at least one subcomponent for a calendar to be compliant
event = Event()
event['summary'] = 'Python meeting about calendaring'
event['uid'] = '42'
event.add('dtstart', datetime(2005, 4, 4, 8, 0, 0))
event["summary"] = "Python meeting about calendaring"
event["uid"] = "42"
event.add("dtstart", datetime(2005, 4, 4, 8, 0, 0))
cal.add_component(event)
assert cal.subcomponents[0].to_ical() == \
b'BEGIN:VEVENT\r\nSUMMARY:Python meeting about calendaring\r\n' \
+ b'DTSTART:20050404T080000\r\nUID:42\r\n' \
+ b'END:VEVENT\r\n'
assert (
cal.subcomponents[0].to_ical()
== b"BEGIN:VEVENT\r\nSUMMARY:Python meeting about calendaring\r\n"
+ b"DTSTART:20050404T080000\r\nUID:42\r\n"
+ b"END:VEVENT\r\n"
)
def test_calendar_with_parsing_errors_includes_all_events(calendars):
@ -376,8 +420,10 @@ def test_calendar_with_parsing_errors_includes_all_events(calendars):
attribute. The error in the following is the third EXDATE: it has an
empty DATE.
"""
event_descriptions = [e['DESCRIPTION'].to_ical() for e in calendars.parsing_error.walk('VEVENT')]
assert event_descriptions == [b'Perfectly OK event', b'Wrong event']
event_descriptions = [
e["DESCRIPTION"].to_ical() for e in calendars.parsing_error.walk("VEVENT")
]
assert event_descriptions == [b"Perfectly OK event", b"Wrong event"]
def test_calendar_with_parsing_errors_has_an_error_in_one_event(calendars):
@ -386,8 +432,8 @@ def test_calendar_with_parsing_errors_has_an_error_in_one_event(calendars):
attribute. The error in the following is the third EXDATE: it has an
empty DATE.
"""
errors = [e.errors for e in calendars.parsing_error.walk('VEVENT')]
assert errors == [[], [('EXDATE', "Expected datetime, date, or time, got: ''")]]
errors = [e.errors for e in calendars.parsing_error.walk("VEVENT")]
assert errors == [[], [("EXDATE", "Expected datetime, date, or time, got: ''")]]
def test_cal_strict_parsing(calendars):
@ -398,21 +444,26 @@ def test_cal_strict_parsing(calendars):
def test_cal_ignore_errors_parsing(calendars, vUTCOffset_ignore_exceptions):
"""If we diable the errors, we should be able to put the calendar back together."""
assert calendars.parsing_error_in_UTC_offset.to_ical() == calendars.parsing_error_in_UTC_offset.raw_ics
assert (
calendars.parsing_error_in_UTC_offset.to_ical()
== calendars.parsing_error_in_UTC_offset.raw_ics
)
@pytest.mark.parametrize(
'calendar, other_calendar',
itertools.product([
'issue_156_RDATE_with_PERIOD_TZID_khal',
'issue_156_RDATE_with_PERIOD_TZID_khal_2',
'issue_178_custom_component_contains_other',
'issue_178_custom_component_inside_other',
'issue_526_calendar_with_events',
'issue_526_calendar_with_different_events',
'issue_526_calendar_with_event_subset',
], repeat=2)
("calendar", "other_calendar"),
itertools.product(
[
"issue_156_RDATE_with_PERIOD_TZID_khal",
"issue_156_RDATE_with_PERIOD_TZID_khal_2",
"issue_178_custom_component_contains_other",
"issue_178_custom_component_inside_other",
"issue_526_calendar_with_events",
"issue_526_calendar_with_different_events",
"issue_526_calendar_with_event_subset",
],
repeat=2,
),
)
def test_comparing_calendars(calendars, calendar, other_calendar, tzp):
are_calendars_equal = calendars[calendar] == calendars[other_calendar]
@ -420,12 +471,19 @@ def test_comparing_calendars(calendars, calendar, other_calendar, tzp):
assert are_calendars_equal == are_calendars_actually_equal
@pytest.mark.parametrize('calendar, shuffeled_calendar', [
(
'issue_526_calendar_with_events',
'issue_526_calendar_with_shuffeled_events',
),
])
def test_calendars_with_same_subcomponents_in_different_order_are_equal(calendars, calendar, shuffeled_calendar):
assert not calendars[calendar].subcomponents == calendars[shuffeled_calendar].subcomponents
@pytest.mark.parametrize(
("calendar", "shuffeled_calendar"),
[
(
"issue_526_calendar_with_events",
"issue_526_calendar_with_shuffeled_events",
),
],
)
def test_calendars_with_same_subcomponents_in_different_order_are_equal(
calendars, calendar, shuffeled_calendar
):
assert (
calendars[calendar].subcomponents != calendars[shuffeled_calendar].subcomponents
)
assert calendars[calendar] == calendars[shuffeled_calendar]

Wyświetl plik

@ -4,64 +4,93 @@ import icalendar
class TestCaselessdict(unittest.TestCase):
def test_caselessdict_canonsort_keys(self):
canonsort_keys = icalendar.caselessdict.canonsort_keys
keys = ['DTEND', 'DTSTAMP', 'DTSTART', 'UID', 'SUMMARY', 'LOCATION']
keys = ["DTEND", "DTSTAMP", "DTSTART", "UID", "SUMMARY", "LOCATION"]
out = canonsort_keys(keys)
self.assertEqual(
out,
['DTEND', 'DTSTAMP', 'DTSTART', 'LOCATION', 'SUMMARY', 'UID']
)
out = canonsort_keys(keys, ('SUMMARY', 'DTSTART', 'DTEND', ))
self.assertEqual(
out,
['SUMMARY', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'UID']
)
out = canonsort_keys(keys, ('UID', 'DTSTART', 'DTEND', ))
self.assertEqual(
out,
['UID', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'SUMMARY']
out, ["DTEND", "DTSTAMP", "DTSTART", "LOCATION", "SUMMARY", "UID"]
)
out = canonsort_keys(
keys,
('UID', 'DTSTART', 'DTEND', 'RRULE', 'EXDATE')
(
"SUMMARY",
"DTSTART",
"DTEND",
),
)
self.assertEqual(
out,
['UID', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'SUMMARY']
out, ["SUMMARY", "DTSTART", "DTEND", "DTSTAMP", "LOCATION", "UID"]
)
out = canonsort_keys(
keys,
(
"UID",
"DTSTART",
"DTEND",
),
)
self.assertEqual(
out, ["UID", "DTSTART", "DTEND", "DTSTAMP", "LOCATION", "SUMMARY"]
)
out = canonsort_keys(keys, ("UID", "DTSTART", "DTEND", "RRULE", "EXDATE"))
self.assertEqual(
out, ["UID", "DTSTART", "DTEND", "DTSTAMP", "LOCATION", "SUMMARY"]
)
def test_caselessdict_canonsort_items(self):
canonsort_items = icalendar.caselessdict.canonsort_items
d = {
'i': 7, 'c': 'at', 'a': 3.5, 'l': (2, 3), 'e': [4, 5], 'n': 13, 'd': {'x': 'y'}, 'r': 1.0,
"i": 7,
"c": "at",
"a": 3.5,
"l": (2, 3),
"e": [4, 5],
"n": 13,
"d": {"x": "y"},
"r": 1.0,
}
out = canonsort_items(d)
self.assertEqual(
out,
[('a', 3.5), ('c', 'at'), ('d', {'x': 'y'}), ('e', [4, 5]),
('i', 7), ('l', (2, 3)), ('n', 13), ('r', 1.0)]
[
("a", 3.5),
("c", "at"),
("d", {"x": "y"}),
("e", [4, 5]),
("i", 7),
("l", (2, 3)),
("n", 13),
("r", 1.0),
],
)
out = canonsort_items(d, ('i', 'c', 'a'))
out = canonsort_items(d, ("i", "c", "a"))
self.assertTrue(
out,
[('i', 7), ('c', 'at'), ('a', 3.5), ('d', {'x': 'y'}),
('e', [4, 5]), ('l', (2, 3)), ('n', 13), ('r', 1.0)]
[
("i", 7),
("c", "at"),
("a", 3.5),
("d", {"x": "y"}),
("e", [4, 5]),
("l", (2, 3)),
("n", 13),
("r", 1.0),
],
)
def test_caselessdict_copy(self):
CaselessDict = icalendar.caselessdict.CaselessDict
original_dict = CaselessDict(key1='val1', key2='val2')
original_dict = CaselessDict(key1="val1", key2="val2")
copied_dict = original_dict.copy()
self.assertEqual(original_dict, copied_dict)
@ -69,31 +98,28 @@ class TestCaselessdict(unittest.TestCase):
def test_CaselessDict(self):
CaselessDict = icalendar.caselessdict.CaselessDict
ncd = CaselessDict(key1='val1', key2='val2')
self.assertEqual(
ncd,
CaselessDict({'KEY2': 'val2', 'KEY1': 'val1'})
)
ncd = CaselessDict(key1="val1", key2="val2")
self.assertEqual(ncd, CaselessDict({"KEY2": "val2", "KEY1": "val1"}))
self.assertEqual(ncd['key1'], 'val1')
self.assertEqual(ncd['KEY1'], 'val1')
self.assertEqual(ncd["key1"], "val1")
self.assertEqual(ncd["KEY1"], "val1")
ncd['KEY3'] = 'val3'
self.assertEqual(ncd['key3'], 'val3')
ncd["KEY3"] = "val3"
self.assertEqual(ncd["key3"], "val3")
self.assertEqual(ncd.setdefault('key3', 'FOUND'), 'val3')
self.assertEqual(ncd.setdefault('key4', 'NOT FOUND'), 'NOT FOUND')
self.assertEqual(ncd['key4'], 'NOT FOUND')
self.assertEqual(ncd.get('key1'), 'val1')
self.assertEqual(ncd.get('key3', 'NOT FOUND'), 'val3')
self.assertEqual(ncd.get('key4', 'NOT FOUND'), 'NOT FOUND')
self.assertTrue('key4' in ncd)
self.assertEqual(ncd.setdefault("key3", "FOUND"), "val3")
self.assertEqual(ncd.setdefault("key4", "NOT FOUND"), "NOT FOUND")
self.assertEqual(ncd["key4"], "NOT FOUND")
self.assertEqual(ncd.get("key1"), "val1")
self.assertEqual(ncd.get("key3", "NOT FOUND"), "val3")
self.assertEqual(ncd.get("key4", "NOT FOUND"), "NOT FOUND")
self.assertTrue("key4" in ncd)
del ncd['key4']
self.assertFalse('key4' in ncd)
del ncd["key4"]
self.assertFalse("key4" in ncd)
ncd.update({'key5': 'val5', 'KEY6': 'val6', 'KEY5': 'val7'})
self.assertEqual(ncd['key6'], 'val6')
ncd.update({"key5": "val5", "KEY6": "val6", "KEY5": "val7"})
self.assertEqual(ncd["key6"], "val6")
keys = sorted(ncd.keys())
self.assertEqual(keys, ['KEY1', 'KEY2', 'KEY3', 'KEY5', 'KEY6'])
self.assertEqual(keys, ["KEY1", "KEY2", "KEY3", "KEY5", "KEY6"])

Wyświetl plik

@ -1,28 +1,30 @@
from icalendar.parser_tools import data_encode
from icalendar.parser_tools import to_unicode
import unittest
from icalendar.parser_tools import data_encode, to_unicode
class TestParserTools(unittest.TestCase):
def test_parser_tools_to_unicode(self):
self.assertEqual(to_unicode(b'spam'), 'spam')
self.assertEqual(to_unicode('spam'), 'spam')
self.assertEqual(to_unicode(b'spam'), 'spam')
self.assertEqual(to_unicode(b'\xc6\xb5'), '\u01b5')
self.assertEqual(to_unicode(b'\xc6\xb5'),
'\u01b5')
self.assertEqual(to_unicode(b'\xc6\xb5', encoding='ascii'), '\u01b5')
self.assertEqual(to_unicode(b"spam"), "spam")
self.assertEqual(to_unicode("spam"), "spam")
self.assertEqual(to_unicode(b"spam"), "spam")
self.assertEqual(to_unicode(b"\xc6\xb5"), "\u01b5")
self.assertEqual(to_unicode(b"\xc6\xb5"), "\u01b5")
self.assertEqual(to_unicode(b"\xc6\xb5", encoding="ascii"), "\u01b5")
self.assertEqual(to_unicode(1), 1)
self.assertEqual(to_unicode(None), None)
def test_parser_tools_data_encode(self):
data1 = {
'k1': 'v1', 'k2': 'v2', 'k3': 'v3',
'li1': ['it1', 'it2', {'k4': 'v4', 'k5': 'v5'}, 123]
"k1": "v1",
"k2": "v2",
"k3": "v3",
"li1": ["it1", "it2", {"k4": "v4", "k5": "v5"}, 123],
}
res = {
b"k3": b"v3",
b"k2": b"v2",
b"k1": b"v1",
b"li1": [b"it1", b"it2", {b"k5": b"v5", b"k4": b"v4"}, 123],
}
res = {b'k3': b'v3', b'k2': b'v2', b'k1': b'v1',
b'li1': [b'it1', b'it2', {b'k5': b'v5', b'k4': b'v4'}, 123]}
self.assertEqual(data_encode(data1), res)

Wyświetl plik

@ -1,12 +1,12 @@
import pytest
import unittest
import pytest
from icalendar.tools import UIDGenerator
class TestTools(unittest.TestCase):
def test_tools_UIDGenerator(self):
# Automatic semi-random uid
g = UIDGenerator()
uid = g.uid()
@ -14,41 +14,68 @@ class TestTools(unittest.TestCase):
txt = uid.to_ical()
length = 15 + 1 + 16 + 1 + 11
self.assertTrue(len(txt) == length)
self.assertTrue(b'@example.com' in txt)
self.assertTrue(b"@example.com" in txt)
# You should at least insert your own hostname to be more compliant
uid = g.uid('Example.ORG')
uid = g.uid("Example.ORG")
txt = uid.to_ical()
self.assertTrue(len(txt) == length)
self.assertTrue(b'@Example.ORG' in txt)
self.assertTrue(b"@Example.ORG" in txt)
# You can also insert a path or similar
uid = g.uid('Example.ORG', '/path/to/content')
uid = g.uid("Example.ORG", "/path/to/content")
txt = uid.to_ical()
self.assertTrue(len(txt) == length)
self.assertTrue(b'-/path/to/content@Example.ORG' in txt)
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"), {},),
])
@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?
"""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

Wyświetl plik

@ -10,12 +10,13 @@ This file should be tests, too:
Hello World!
"""
import doctest
import os
import pytest
import importlib
import os
import sys
import re
import pytest
HERE = os.path.dirname(__file__) or "."
ICALENDAR_PATH = os.path.dirname(HERE)
@ -23,17 +24,21 @@ ICALENDAR_PATH = os.path.dirname(HERE)
PYTHON_FILES = [
"/".join((dirpath, filename))
for dirpath, dirnames, filenames in os.walk(ICALENDAR_PATH)
for filename in filenames if filename.lower().endswith(".py") and 'fuzzing' not in dirpath
for filename in filenames
if filename.lower().endswith(".py") and "fuzzing" not in dirpath
]
MODULE_NAMES = [
"icalendar" + python_file[len(ICALENDAR_PATH):-3].replace("\\", "/").replace("/", ".")
"icalendar"
+ python_file[len(ICALENDAR_PATH) : -3].replace("\\", "/").replace("/", ".")
for python_file in PYTHON_FILES
]
def test_this_module_is_among_them():
assert __name__ in MODULE_NAMES
@pytest.mark.parametrize("module_name", MODULE_NAMES)
def test_docstring_of_python_file(module_name, env_for_doctest):
"""This test runs doctest on the Python module."""
@ -58,12 +63,18 @@ try:
if filename.lower().endswith(".rst")
]
except FileNotFoundError:
raise OSError("Could not find the documentation - remove the build folder and try again.")
raise OSError(
"Could not find the documentation - remove the build folder and try again."
)
@pytest.mark.parametrize("filename", [
"README.rst",
"index.rst",
])
@pytest.mark.parametrize(
"filename",
[
"README.rst",
"index.rst",
],
)
def test_files_is_included(filename):
assert any(path.endswith(filename) for path in DOCUMENT_PATHS)
@ -76,14 +87,18 @@ def test_documentation_file(document, zoneinfo_only, env_for_doctest, tzp):
"""
try:
# set raise_on_error to False if you wand to see the error for debug
test_result = doctest.testfile(document, module_relative=False, globs=env_for_doctest, raise_on_error=True)
test_result = doctest.testfile(
document, module_relative=False, globs=env_for_doctest, raise_on_error=True
)
except doctest.UnexpectedException as e:
ty, err, tb = e.exc_info
if issubclass(ty, ModuleNotFoundError) and err.name == "pytz":
pytest.skip("pytz not installed, skipping this file.")
finally:
tzp.use_zoneinfo()
assert test_result.failed == 0, f"{test_result.failed} errors in {os.path.basename(document)}"
assert (
test_result.failed == 0
), f"{test_result.failed} errors in {os.path.basename(document)}"
def test_can_import_zoneinfo(env_for_doctest):