|
@@ -1,951 +0,0 @@
|
|
|
-# -*- coding: utf-8 -*-
|
|
|
|
|
-
|
|
|
|
|
-from __future__ import print_function
|
|
|
|
|
-
|
|
|
|
|
-import datetime
|
|
|
|
|
-import dateutil
|
|
|
|
|
-import os
|
|
|
|
|
-import re
|
|
|
|
|
-import sys
|
|
|
|
|
-import unittest
|
|
|
|
|
-import json
|
|
|
|
|
-
|
|
|
|
|
-from dateutil.tz import tzutc
|
|
|
|
|
-from dateutil.rrule import rrule, rruleset, WEEKLY, MONTHLY
|
|
|
|
|
-
|
|
|
|
|
-from radicale_vobject import base, iCalendar
|
|
|
|
|
-from radicale_vobject import icalendar
|
|
|
|
|
-
|
|
|
|
|
-from radicale_vobject.base import __behaviorRegistry as behavior_registry
|
|
|
|
|
-from radicale_vobject.base import ContentLine, parseLine, ParseError
|
|
|
|
|
-from radicale_vobject.base import readComponents, textLineToContentLine
|
|
|
|
|
-
|
|
|
|
|
-from radicale_vobject.change_tz import change_tz
|
|
|
|
|
-
|
|
|
|
|
-from radicale_vobject.icalendar import MultiDateBehavior, PeriodBehavior, \
|
|
|
|
|
- RecurringComponent, utc
|
|
|
|
|
-from radicale_vobject.icalendar import parseDtstart, stringToTextValues, \
|
|
|
|
|
- stringToPeriod, timedeltaToString
|
|
|
|
|
-
|
|
|
|
|
-two_hours = datetime.timedelta(hours=2)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-def get_test_filepath(path):
|
|
|
|
|
- """
|
|
|
|
|
- Helper function to get the filepath of test files.
|
|
|
|
|
- """
|
|
|
|
|
- return os.path.join(os.path.dirname(__file__), "test_files", path)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-def get_test_file(path):
|
|
|
|
|
- """
|
|
|
|
|
- Helper function to open and read test files.
|
|
|
|
|
- """
|
|
|
|
|
- filepath = get_test_filepath(path)
|
|
|
|
|
- if sys.version_info[0] < 3:
|
|
|
|
|
- # On python 2, this library operates on bytes.
|
|
|
|
|
- f = open(filepath, 'r')
|
|
|
|
|
- else:
|
|
|
|
|
- # On python 3, it operates on unicode. We need to specify an encoding
|
|
|
|
|
- # for systems for which the preferred encoding isn't utf-8 (e.g windows)
|
|
|
|
|
- f = open(filepath, 'r', encoding='utf-8')
|
|
|
|
|
- text = f.read()
|
|
|
|
|
- f.close()
|
|
|
|
|
- return text
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TestCalendarSerializing(unittest.TestCase):
|
|
|
|
|
- """
|
|
|
|
|
- Test creating an iCalendar file
|
|
|
|
|
- """
|
|
|
|
|
- max_diff = None
|
|
|
|
|
-
|
|
|
|
|
- def test_scratchbuild(self):
|
|
|
|
|
- """
|
|
|
|
|
- CreateCalendar 2.0 format from scratch
|
|
|
|
|
- """
|
|
|
|
|
- test_cal = get_test_file("simple_2_0_test.ics")
|
|
|
|
|
- cal = base.newFromBehavior('vcalendar', '2.0')
|
|
|
|
|
- cal.add('vevent')
|
|
|
|
|
- cal.vevent.add('dtstart').value = datetime.datetime(2006, 5, 9)
|
|
|
|
|
- cal.vevent.add('description').value = "Test event"
|
|
|
|
|
- cal.vevent.add('created').value = \
|
|
|
|
|
- datetime.datetime(2006, 1, 1, 10,
|
|
|
|
|
- tzinfo=dateutil.tz.tzical(
|
|
|
|
|
- get_test_filepath("timezones.ics")).get('US/Pacific'))
|
|
|
|
|
- cal.vevent.add('uid').value = "Not very random UID"
|
|
|
|
|
- cal.vevent.add('dtstamp').value = datetime.datetime(2017, 6, 26, 0, tzinfo=tzutc())
|
|
|
|
|
-
|
|
|
|
|
- # Note we're normalizing line endings, because no one got time for that.
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- cal.serialize().replace('\r\n', '\n'),
|
|
|
|
|
- test_cal.replace('\r\n', '\n')
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_unicode(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test unicode characters
|
|
|
|
|
- """
|
|
|
|
|
- test_cal = get_test_file("utf8_test.ics")
|
|
|
|
|
- vevent = base.readOne(test_cal).vevent
|
|
|
|
|
- vevent2 = base.readOne(vevent.serialize())
|
|
|
|
|
- self.assertEqual(str(vevent), str(vevent2))
|
|
|
|
|
-
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- vevent.summary.value,
|
|
|
|
|
- 'The title こんにちはキティ'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- if sys.version_info[0] < 3:
|
|
|
|
|
- test_cal = test_cal.decode('utf-8')
|
|
|
|
|
- vevent = base.readOne(test_cal).vevent
|
|
|
|
|
- vevent2 = base.readOne(vevent.serialize())
|
|
|
|
|
- self.assertEqual(str(vevent), str(vevent2))
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- vevent.summary.value,
|
|
|
|
|
- u'The title こんにちはキティ'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_wrapping(self):
|
|
|
|
|
- """
|
|
|
|
|
- Should support input file with a long text field covering multiple lines
|
|
|
|
|
- """
|
|
|
|
|
- test_journal = get_test_file("journal.ics")
|
|
|
|
|
- vobj = base.readOne(test_journal)
|
|
|
|
|
- vjournal = base.readOne(vobj.serialize())
|
|
|
|
|
- self.assertTrue('Joe, Lisa, and Bob' in vjournal.description.value)
|
|
|
|
|
- self.assertTrue('Tuesday.\n2.' in vjournal.description.value)
|
|
|
|
|
-
|
|
|
|
|
- def test_multiline(self):
|
|
|
|
|
- """
|
|
|
|
|
- Multi-text serialization test
|
|
|
|
|
- """
|
|
|
|
|
- category = base.newFromBehavior('categories')
|
|
|
|
|
- category.value = ['Random category']
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- category.serialize().strip(),
|
|
|
|
|
- "CATEGORIES:Random category"
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- category.value.append('Other category')
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- category.serialize().strip(),
|
|
|
|
|
- "CATEGORIES:Random category,Other category"
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_semicolon_separated(self):
|
|
|
|
|
- """
|
|
|
|
|
- Semi-colon separated multi-text serialization test
|
|
|
|
|
- """
|
|
|
|
|
- request_status = base.newFromBehavior('request-status')
|
|
|
|
|
- request_status.value = ['5.1', 'Service unavailable']
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- request_status.serialize().strip(),
|
|
|
|
|
- "REQUEST-STATUS:5.1;Service unavailable"
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- @staticmethod
|
|
|
|
|
- def test_unicode_multiline():
|
|
|
|
|
- """
|
|
|
|
|
- Test multiline unicode characters
|
|
|
|
|
- """
|
|
|
|
|
- cal = iCalendar()
|
|
|
|
|
- cal.add('method').value = 'REQUEST'
|
|
|
|
|
- cal.add('vevent')
|
|
|
|
|
- cal.vevent.add('created').value = datetime.datetime.now()
|
|
|
|
|
- cal.vevent.add('summary').value = 'Классное событие'
|
|
|
|
|
- cal.vevent.add('description').value = ('Классное событие Классное событие Классное событие Классное событие '
|
|
|
|
|
- 'Классное событие Классsdssdное событие')
|
|
|
|
|
-
|
|
|
|
|
- # json tries to encode as utf-8 and it would break if some chars could not be encoded
|
|
|
|
|
- json.dumps(cal.serialize())
|
|
|
|
|
-
|
|
|
|
|
- @staticmethod
|
|
|
|
|
- def test_ical_to_hcal():
|
|
|
|
|
- """
|
|
|
|
|
- Serializing iCalendar to hCalendar.
|
|
|
|
|
-
|
|
|
|
|
- Since Hcalendar is experimental and the behavior doesn't seem to want to load,
|
|
|
|
|
- This test will have to wait.
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- tzs = dateutil.tz.tzical(get_test_filepath("timezones.ics"))
|
|
|
|
|
- cal = base.newFromBehavior('hcalendar')
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(cal.behavior),
|
|
|
|
|
- "<class 'radicale_vobject.hcalendar.HCalendar'>"
|
|
|
|
|
- )
|
|
|
|
|
- cal.add('vevent')
|
|
|
|
|
- cal.vevent.add('summary').value = "this is a note"
|
|
|
|
|
- cal.vevent.add('url').value = "http://microformats.org/code/hcalendar/creator"
|
|
|
|
|
- cal.vevent.add('dtstart').value = datetime.date(2006,2,27)
|
|
|
|
|
- cal.vevent.add('location').value = "a place"
|
|
|
|
|
- cal.vevent.add('dtend').value = datetime.date(2006,2,27) + datetime.timedelta(days = 2)
|
|
|
|
|
-
|
|
|
|
|
- event2 = cal.add('vevent')
|
|
|
|
|
- event2.add('summary').value = "Another one"
|
|
|
|
|
- event2.add('description').value = "The greatest thing ever!"
|
|
|
|
|
- event2.add('dtstart').value = datetime.datetime(1998, 12, 17, 16, 42, tzinfo = tzs.get('US/Pacific'))
|
|
|
|
|
- event2.add('location').value = "somewhere else"
|
|
|
|
|
- event2.add('dtend').value = event2.dtstart.value + datetime.timedelta(days = 6)
|
|
|
|
|
- hcal = cal.serialize()
|
|
|
|
|
- """
|
|
|
|
|
- #self.assertEqual(
|
|
|
|
|
- # str(hcal),
|
|
|
|
|
- # """<span class="vevent">
|
|
|
|
|
- # <a class="url" href="http://microformats.org/code/hcalendar/creator">
|
|
|
|
|
- # <span class="summary">this is a note</span>:
|
|
|
|
|
- # <abbr class="dtstart", title="20060227">Monday, February 27</abbr>
|
|
|
|
|
- # - <abbr class="dtend", title="20060301">Tuesday, February 28</abbr>
|
|
|
|
|
- # at <span class="location">a place</span>
|
|
|
|
|
- # </a>
|
|
|
|
|
- # </span>
|
|
|
|
|
- # <span class="vevent">
|
|
|
|
|
- # <span class="summary">Another one</span>:
|
|
|
|
|
- # <abbr class="dtstart", title="19981217T164200-0800">Thursday, December 17, 16:42</abbr>
|
|
|
|
|
- # - <abbr class="dtend", title="19981223T164200-0800">Wednesday, December 23, 16:42</abbr>
|
|
|
|
|
- # at <span class="location">somewhere else</span>
|
|
|
|
|
- # <div class="description">The greatest thing ever!</div>
|
|
|
|
|
- # </span>
|
|
|
|
|
- # """
|
|
|
|
|
- #)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TestBehaviors(unittest.TestCase):
|
|
|
|
|
- """
|
|
|
|
|
- Test Behaviors
|
|
|
|
|
- """
|
|
|
|
|
- def test_general_behavior(self):
|
|
|
|
|
- """
|
|
|
|
|
- Tests for behavior registry, getting and creating a behavior.
|
|
|
|
|
- """
|
|
|
|
|
- # Check expected behavior registry.
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- sorted(behavior_registry.keys()),
|
|
|
|
|
- ['', 'ACTION', 'ADR', 'AVAILABLE', 'BUSYTYPE', 'CALSCALE',
|
|
|
|
|
- 'CATEGORIES', 'CLASS', 'COMMENT', 'COMPLETED', 'CONTACT',
|
|
|
|
|
- 'CREATED', 'DAYLIGHT', 'DESCRIPTION', 'DTEND', 'DTSTAMP',
|
|
|
|
|
- 'DTSTART', 'DUE', 'DURATION', 'EXDATE', 'EXRULE', 'FN', 'FREEBUSY',
|
|
|
|
|
- 'LABEL', 'LAST-MODIFIED', 'LOCATION', 'METHOD', 'N', 'ORG',
|
|
|
|
|
- 'PHOTO', 'PRODID', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO',
|
|
|
|
|
- 'REQUEST-STATUS', 'RESOURCES', 'REV', 'RRULE', 'STANDARD', 'STATUS',
|
|
|
|
|
- 'SUMMARY', 'TRANSP', 'TRIGGER', 'UID', 'VALARM', 'VAVAILABILITY',
|
|
|
|
|
- 'VCALENDAR', 'VCARD', 'VEVENT', 'VFREEBUSY', 'VJOURNAL',
|
|
|
|
|
- 'VTIMEZONE', 'VTODO']
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- # test get_behavior
|
|
|
|
|
- behavior = base.getBehavior('VCALENDAR')
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(behavior),
|
|
|
|
|
- "<class 'radicale_vobject.icalendar.VCalendar2_0'>"
|
|
|
|
|
- )
|
|
|
|
|
- self.assertTrue(behavior.isComponent)
|
|
|
|
|
-
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- base.getBehavior("invalid_name"),
|
|
|
|
|
- None
|
|
|
|
|
- )
|
|
|
|
|
- # test for ContentLine (not a component)
|
|
|
|
|
- non_component_behavior = base.getBehavior('RDATE')
|
|
|
|
|
- self.assertFalse(non_component_behavior.isComponent)
|
|
|
|
|
-
|
|
|
|
|
- def test_MultiDateBehavior(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test MultiDateBehavior
|
|
|
|
|
- """
|
|
|
|
|
- parseRDate = MultiDateBehavior.transformToNative
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(parseRDate(textLineToContentLine("RDATE;VALUE=DATE:19970304,19970504,19970704,19970904"))),
|
|
|
|
|
- "<RDATE{'VALUE': ['DATE']}[datetime.date(1997, 3, 4), datetime.date(1997, 5, 4), datetime.date(1997, 7, 4), datetime.date(1997, 9, 4)]>"
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(parseRDate(textLineToContentLine("RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H"))),
|
|
|
|
|
- "<RDATE{'VALUE': ['PERIOD']}[(datetime.datetime(1996, 4, 3, 2, 0, tzinfo=tzutc()), datetime.datetime(1996, 4, 3, 4, 0, tzinfo=tzutc())), (datetime.datetime(1996, 4, 4, 1, 0, tzinfo=tzutc()), datetime.timedelta(0, 10800))]>"
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_periodBehavior(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test PeriodBehavior
|
|
|
|
|
- """
|
|
|
|
|
- line = ContentLine('test', [], '', isNative=True)
|
|
|
|
|
- line.behavior = PeriodBehavior
|
|
|
|
|
- line.value = [(datetime.datetime(2006, 2, 16, 10), two_hours)]
|
|
|
|
|
-
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- line.transformFromNative().value,
|
|
|
|
|
- '20060216T100000/PT2H'
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- line.transformToNative().value,
|
|
|
|
|
- [(datetime.datetime(2006, 2, 16, 10, 0),
|
|
|
|
|
- datetime.timedelta(0, 7200))]
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- line.value.append((datetime.datetime(2006, 5, 16, 10), two_hours))
|
|
|
|
|
-
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- line.serialize().strip(),
|
|
|
|
|
- 'TEST:20060216T100000/PT2H,20060516T100000/PT2H'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TestVTodo(unittest.TestCase):
|
|
|
|
|
- """
|
|
|
|
|
- VTodo Tests
|
|
|
|
|
- """
|
|
|
|
|
- def test_vtodo(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test VTodo
|
|
|
|
|
- """
|
|
|
|
|
- vtodo = get_test_file("vtodo.ics")
|
|
|
|
|
- obj = base.readOne(vtodo)
|
|
|
|
|
- obj.vtodo.add('completed')
|
|
|
|
|
- obj.vtodo.completed.value = datetime.datetime(2015,5,5,13,30)
|
|
|
|
|
- self.assertEqual(obj.vtodo.completed.serialize()[0:23],
|
|
|
|
|
- 'COMPLETED:20150505T1330')
|
|
|
|
|
- obj = base.readOne(obj.serialize())
|
|
|
|
|
- self.assertEqual(obj.vtodo.completed.value,
|
|
|
|
|
- datetime.datetime(2015,5,5,13,30))
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TestVobject(unittest.TestCase):
|
|
|
|
|
- """
|
|
|
|
|
- VObject Tests
|
|
|
|
|
- """
|
|
|
|
|
- max_diff = None
|
|
|
|
|
-
|
|
|
|
|
- @classmethod
|
|
|
|
|
- def setUpClass(cls):
|
|
|
|
|
- """
|
|
|
|
|
- Method for setting up class fixture before running tests in the class.
|
|
|
|
|
- Fetches test file.
|
|
|
|
|
- """
|
|
|
|
|
- cls.simple_test_cal = get_test_file("simple_test.ics")
|
|
|
|
|
-
|
|
|
|
|
- def test_readComponents(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test if reading components correctly
|
|
|
|
|
- """
|
|
|
|
|
- cal = next(readComponents(self.simple_test_cal))
|
|
|
|
|
-
|
|
|
|
|
- self.assertEqual(str(cal), "<VCALENDAR| [<VEVENT| [<SUMMARY{'BLAH': ['hi!']}Bastille Day Party>]>]>")
|
|
|
|
|
- self.assertEqual(str(cal.vevent.summary), "<SUMMARY{'BLAH': ['hi!']}Bastille Day Party>")
|
|
|
|
|
-
|
|
|
|
|
- def test_parseLine(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test line parsing
|
|
|
|
|
- """
|
|
|
|
|
- self.assertEqual(parseLine("BLAH:"), ('BLAH', [], '', None))
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- parseLine("RDATE:VALUE=DATE:19970304,19970504,19970704,19970904"),
|
|
|
|
|
- ('RDATE', [], 'VALUE=DATE:19970304,19970504,19970704,19970904', None)
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- parseLine('DESCRIPTION;ALTREP="http://www.wiz.org":The Fall 98 Wild Wizards Conference - - Las Vegas, NV, USA'),
|
|
|
|
|
- ('DESCRIPTION', [['ALTREP', 'http://www.wiz.org']], 'The Fall 98 Wild Wizards Conference - - Las Vegas, NV, USA', None)
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- parseLine("EMAIL;PREF;INTERNET:john@nowhere.com"),
|
|
|
|
|
- ('EMAIL', [['PREF'], ['INTERNET']], 'john@nowhere.com', None)
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- parseLine('EMAIL;TYPE="blah",hah;INTERNET="DIGI",DERIDOO:john@nowhere.com'),
|
|
|
|
|
- ('EMAIL', [['TYPE', 'blah', 'hah'], ['INTERNET', 'DIGI', 'DERIDOO']], 'john@nowhere.com', None)
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- parseLine('item1.ADR;type=HOME;type=pref:;;Reeperbahn 116;Hamburg;;20359;'),
|
|
|
|
|
- ('ADR', [['type', 'HOME'], ['type', 'pref']], ';;Reeperbahn 116;Hamburg;;20359;', 'item1')
|
|
|
|
|
- )
|
|
|
|
|
- self.assertRaises(ParseError, parseLine, ":")
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TestGeneralFileParsing(unittest.TestCase):
|
|
|
|
|
- """
|
|
|
|
|
- General tests for parsing ics files.
|
|
|
|
|
- """
|
|
|
|
|
- def test_readOne(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test reading first component of ics
|
|
|
|
|
- """
|
|
|
|
|
- cal = get_test_file("silly_test.ics")
|
|
|
|
|
- silly = base.readOne(cal)
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(silly),
|
|
|
|
|
- "<SILLYPROFILE| [<MORESTUFF{}this line is not folded, but in practice probably ought to be, as it is exceptionally long, and moreover demonstratively stupid>, <SILLYNAME{}name>, <STUFF{}foldedline>]>"
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(silly.stuff),
|
|
|
|
|
- "<STUFF{}foldedline>"
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_importing(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test importing ics
|
|
|
|
|
- """
|
|
|
|
|
- cal = get_test_file("standard_test.ics")
|
|
|
|
|
- c = base.readOne(cal, validate=True)
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(c.vevent.valarm.trigger),
|
|
|
|
|
- "<TRIGGER{}-1 day, 0:00:00>"
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(c.vevent.dtstart.value),
|
|
|
|
|
- "2002-10-28 14:00:00-08:00"
|
|
|
|
|
- )
|
|
|
|
|
- self.assertTrue(
|
|
|
|
|
- isinstance(c.vevent.dtstart.value, datetime.datetime)
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(c.vevent.dtend.value),
|
|
|
|
|
- "2002-10-28 15:00:00-08:00"
|
|
|
|
|
- )
|
|
|
|
|
- self.assertTrue(
|
|
|
|
|
- isinstance(c.vevent.dtend.value, datetime.datetime)
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- c.vevent.dtstamp.value,
|
|
|
|
|
- datetime.datetime(2002, 10, 28, 1, 17, 6, tzinfo=tzutc())
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- vevent = c.vevent.transformFromNative()
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(vevent.rrule),
|
|
|
|
|
- "<RRULE{}FREQ=Weekly;COUNT=10>"
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_bad_stream(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test bad ics stream
|
|
|
|
|
- """
|
|
|
|
|
- cal = get_test_file("badstream.ics")
|
|
|
|
|
- self.assertRaises(ParseError, base.readOne, cal)
|
|
|
|
|
-
|
|
|
|
|
- def test_bad_line(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test bad line in ics file
|
|
|
|
|
- """
|
|
|
|
|
- cal = get_test_file("badline.ics")
|
|
|
|
|
- self.assertRaises(ParseError, base.readOne, cal)
|
|
|
|
|
-
|
|
|
|
|
- newcal = base.readOne(cal, ignoreUnreadable=True)
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(newcal.vevent.x_bad_underscore),
|
|
|
|
|
- '<X-BAD-UNDERSCORE{}TRUE>'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_parseParams(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test parsing parameters
|
|
|
|
|
- """
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- base.parseParams(';ALTREP="http://www.wiz.org"'),
|
|
|
|
|
- [['ALTREP', 'http://www.wiz.org']]
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- base.parseParams(';ALTREP="http://www.wiz.org;;",Blah,Foo;NEXT=Nope;BAR'),
|
|
|
|
|
- [['ALTREP', 'http://www.wiz.org;;', 'Blah', 'Foo'],
|
|
|
|
|
- ['NEXT', 'Nope'], ['BAR']]
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TestVcards(unittest.TestCase):
|
|
|
|
|
- """
|
|
|
|
|
- Test VCards
|
|
|
|
|
- """
|
|
|
|
|
- @classmethod
|
|
|
|
|
- def setUpClass(cls):
|
|
|
|
|
- """
|
|
|
|
|
- Method for setting up class fixture before running tests in the class.
|
|
|
|
|
- Fetches test file.
|
|
|
|
|
- """
|
|
|
|
|
- cls.test_file = get_test_file("vcard_with_groups.ics")
|
|
|
|
|
- cls.card = base.readOne(cls.test_file)
|
|
|
|
|
-
|
|
|
|
|
- def test_vcard_creation(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test creating a vCard
|
|
|
|
|
- """
|
|
|
|
|
- vcard = base.newFromBehavior('vcard', '3.0')
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(vcard),
|
|
|
|
|
- "<VCARD| []>"
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_default_behavior(self):
|
|
|
|
|
- """
|
|
|
|
|
- Default behavior test.
|
|
|
|
|
- """
|
|
|
|
|
- card = self.card
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- base.getBehavior('note'),
|
|
|
|
|
- None
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(card.note.value),
|
|
|
|
|
- "The Mayor of the great city of Goerlitz in the great country of Germany.\nNext line."
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_with_groups(self):
|
|
|
|
|
- """
|
|
|
|
|
- vCard groups test
|
|
|
|
|
- """
|
|
|
|
|
- card = self.card
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(card.group),
|
|
|
|
|
- 'home'
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(card.tel.group),
|
|
|
|
|
- 'home'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- card.group = card.tel.group = 'new'
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(card.tel.serialize().strip()),
|
|
|
|
|
- 'new.TEL;TYPE=fax,voice,msg:+49 3581 123456'
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(card.serialize().splitlines()[0]),
|
|
|
|
|
- 'new.BEGIN:VCARD'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- def test_vcard_3_parsing(self):
|
|
|
|
|
- """
|
|
|
|
|
- VCARD 3.0 parse test
|
|
|
|
|
- """
|
|
|
|
|
- test_file = get_test_file("simple_3_0_test.ics")
|
|
|
|
|
- card = base.readOne(test_file)
|
|
|
|
|
- # value not rendering correctly?
|
|
|
|
|
- #self.assertEqual(
|
|
|
|
|
- # card.adr.value,
|
|
|
|
|
- # "<Address: Haight Street 512;\nEscape, Test\nNovosibirsk, 80214\nGnuland>"
|
|
|
|
|
- #)
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- card.org.value,
|
|
|
|
|
- ["University of Novosibirsk", "Department of Octopus Parthenogenesis"]
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- for _ in range(3):
|
|
|
|
|
- new_card = base.readOne(card.serialize())
|
|
|
|
|
- self.assertEqual(new_card.org.value, card.org.value)
|
|
|
|
|
- card = new_card
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TestIcalendar(unittest.TestCase):
|
|
|
|
|
- """
|
|
|
|
|
- Tests for icalendar.py
|
|
|
|
|
- """
|
|
|
|
|
- max_diff = None
|
|
|
|
|
- def test_parseDTStart(self):
|
|
|
|
|
- """
|
|
|
|
|
- Should take a content line and return a datetime object.
|
|
|
|
|
- """
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- parseDtstart(textLineToContentLine("DTSTART:20060509T000000")),
|
|
|
|
|
- datetime.datetime(2006, 5, 9, 0, 0)
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_regexes(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test regex patterns
|
|
|
|
|
- """
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- re.findall(base.patterns['name'], '12foo-bar:yay'),
|
|
|
|
|
- ['12foo-bar', 'yay']
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- re.findall(base.patterns['safe_char'], 'a;b"*,cd'),
|
|
|
|
|
- ['a', 'b', '*', 'c', 'd']
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- re.findall(base.patterns['qsafe_char'], 'a;b"*,cd'),
|
|
|
|
|
- ['a', ';', 'b', '*', ',', 'c', 'd']
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- re.findall(base.patterns['param_value'],
|
|
|
|
|
- '"quoted";not-quoted;start"after-illegal-quote',
|
|
|
|
|
- re.VERBOSE),
|
|
|
|
|
- ['"quoted"', '', 'not-quoted', '', 'start', '',
|
|
|
|
|
- 'after-illegal-quote', '']
|
|
|
|
|
- )
|
|
|
|
|
- match = base.line_re.match('TEST;ALTREP="http://www.wiz.org":value:;"')
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- match.group('value'),
|
|
|
|
|
- 'value:;"'
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- match.group('name'),
|
|
|
|
|
- 'TEST'
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- match.group('params'),
|
|
|
|
|
- ';ALTREP="http://www.wiz.org"'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_stringToTextValues(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test string lists
|
|
|
|
|
- """
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- stringToTextValues(''),
|
|
|
|
|
- ['']
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- stringToTextValues('abcd,efgh'),
|
|
|
|
|
- ['abcd', 'efgh']
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_stringToPeriod(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test datetime strings
|
|
|
|
|
- """
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- stringToPeriod("19970101T180000Z/19970102T070000Z"),
|
|
|
|
|
- (datetime.datetime(1997, 1, 1, 18, 0, tzinfo=tzutc()),
|
|
|
|
|
- datetime.datetime(1997, 1, 2, 7, 0, tzinfo=tzutc()))
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- stringToPeriod("19970101T180000Z/PT1H"),
|
|
|
|
|
- (datetime.datetime(1997, 1, 1, 18, 0, tzinfo=tzutc()),
|
|
|
|
|
- datetime.timedelta(0, 3600))
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_timedeltaToString(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test timedelta strings
|
|
|
|
|
- """
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- timedeltaToString(two_hours),
|
|
|
|
|
- 'PT2H'
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- timedeltaToString(datetime.timedelta(minutes=20)),
|
|
|
|
|
- 'PT20M'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_vtimezone_creation(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test timezones
|
|
|
|
|
- """
|
|
|
|
|
- tzs = dateutil.tz.tzical(get_test_filepath("timezones.ics"))
|
|
|
|
|
- pacific = icalendar.TimezoneComponent(tzs.get('US/Pacific'))
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(pacific),
|
|
|
|
|
- "<VTIMEZONE | <TZID{}US/Pacific>>"
|
|
|
|
|
- )
|
|
|
|
|
- santiago = icalendar.TimezoneComponent(tzs.get('Santiago'))
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- str(santiago),
|
|
|
|
|
- "<VTIMEZONE | <TZID{}Santiago>>"
|
|
|
|
|
- )
|
|
|
|
|
- for year in range(2001, 2010):
|
|
|
|
|
- for month in (2, 9):
|
|
|
|
|
- dt = datetime.datetime(year, month, 15,
|
|
|
|
|
- tzinfo=tzs.get('Santiago'))
|
|
|
|
|
- self.assertTrue(dt.replace(tzinfo=tzs.get('Santiago')), dt)
|
|
|
|
|
-
|
|
|
|
|
- @staticmethod
|
|
|
|
|
- def test_timezone_serializing():
|
|
|
|
|
- """
|
|
|
|
|
- Serializing with timezones test
|
|
|
|
|
- """
|
|
|
|
|
- tzs = dateutil.tz.tzical(get_test_filepath("timezones.ics"))
|
|
|
|
|
- pacific = tzs.get('US/Pacific')
|
|
|
|
|
- cal = base.Component('VCALENDAR')
|
|
|
|
|
- cal.setBehavior(icalendar.VCalendar2_0)
|
|
|
|
|
- ev = cal.add('vevent')
|
|
|
|
|
- ev.add('dtstart').value = datetime.datetime(2005, 10, 12, 9,
|
|
|
|
|
- tzinfo=pacific)
|
|
|
|
|
- evruleset = rruleset()
|
|
|
|
|
- evruleset.rrule(rrule(WEEKLY, interval=2, byweekday=[2,4],
|
|
|
|
|
- until=datetime.datetime(2005, 12, 15, 9)))
|
|
|
|
|
- evruleset.rrule(rrule(MONTHLY, bymonthday=[-1,-5]))
|
|
|
|
|
- evruleset.exdate(datetime.datetime(2005, 10, 14, 9, tzinfo=pacific))
|
|
|
|
|
- ev.rruleset = evruleset
|
|
|
|
|
- ev.add('duration').value = datetime.timedelta(hours=1)
|
|
|
|
|
-
|
|
|
|
|
- apple = tzs.get('America/Montreal')
|
|
|
|
|
- ev.dtstart.value = datetime.datetime(2005, 10, 12, 9, tzinfo=apple)
|
|
|
|
|
-
|
|
|
|
|
- def test_pytz_timezone_serializing(self):
|
|
|
|
|
- """
|
|
|
|
|
- Serializing with timezones from pytz test
|
|
|
|
|
- """
|
|
|
|
|
- try:
|
|
|
|
|
- import pytz
|
|
|
|
|
- except ImportError:
|
|
|
|
|
- return self.skipTest("pytz not installed") # NOQA
|
|
|
|
|
-
|
|
|
|
|
- # Avoid conflicting cached tzinfo from other tests
|
|
|
|
|
- def unregister_tzid(tzid):
|
|
|
|
|
- """Clear tzid from icalendar TZID registry"""
|
|
|
|
|
- if icalendar.getTzid(tzid, False):
|
|
|
|
|
- icalendar.registerTzid(tzid, None)
|
|
|
|
|
-
|
|
|
|
|
- unregister_tzid('US/Eastern')
|
|
|
|
|
- eastern = pytz.timezone('US/Eastern')
|
|
|
|
|
- cal = base.Component('VCALENDAR')
|
|
|
|
|
- cal.setBehavior(icalendar.VCalendar2_0)
|
|
|
|
|
- ev = cal.add('vevent')
|
|
|
|
|
- ev.add('dtstart').value = eastern.localize(
|
|
|
|
|
- datetime.datetime(2008, 10, 12, 9))
|
|
|
|
|
- serialized = cal.serialize()
|
|
|
|
|
-
|
|
|
|
|
- expected_vtimezone = get_test_file("tz_us_eastern.ics")
|
|
|
|
|
- self.assertIn(
|
|
|
|
|
- expected_vtimezone.replace('\r\n', '\n'),
|
|
|
|
|
- serialized.replace('\r\n', '\n')
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- # Exhaustively test all zones (just looking for no errors)
|
|
|
|
|
- for tzname in pytz.all_timezones:
|
|
|
|
|
- unregister_tzid(tzname)
|
|
|
|
|
- tz = icalendar.TimezoneComponent(tzinfo=pytz.timezone(tzname))
|
|
|
|
|
- tz.serialize()
|
|
|
|
|
-
|
|
|
|
|
- def test_freeBusy(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test freebusy components
|
|
|
|
|
- """
|
|
|
|
|
- test_cal = get_test_file("freebusy.ics")
|
|
|
|
|
-
|
|
|
|
|
- vfb = base.newFromBehavior('VFREEBUSY')
|
|
|
|
|
- vfb.add('uid').value = 'test'
|
|
|
|
|
- vfb.add('dtstamp').value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
|
|
|
|
|
- vfb.add('dtstart').value = datetime.datetime(2006, 2, 16, 1, tzinfo=utc)
|
|
|
|
|
- vfb.add('dtend').value = vfb.dtstart.value + two_hours
|
|
|
|
|
- vfb.add('freebusy').value = [(vfb.dtstart.value, two_hours / 2)]
|
|
|
|
|
- vfb.add('freebusy').value = [(vfb.dtstart.value, vfb.dtend.value)]
|
|
|
|
|
-
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- vfb.serialize().replace('\r\n', '\n'),
|
|
|
|
|
- test_cal.replace('\r\n', '\n')
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_availablity(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test availability components
|
|
|
|
|
- """
|
|
|
|
|
- test_cal = get_test_file("availablity.ics")
|
|
|
|
|
-
|
|
|
|
|
- vcal = base.newFromBehavior('VAVAILABILITY')
|
|
|
|
|
- vcal.add('uid').value = 'test'
|
|
|
|
|
- vcal.add('dtstamp').value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
|
|
|
|
|
- vcal.add('dtstart').value = datetime.datetime(2006, 2, 16, 0, tzinfo=utc)
|
|
|
|
|
- vcal.add('dtend').value = datetime.datetime(2006, 2, 17, 0, tzinfo=utc)
|
|
|
|
|
- vcal.add('busytype').value = "BUSY"
|
|
|
|
|
-
|
|
|
|
|
- av = base.newFromBehavior('AVAILABLE')
|
|
|
|
|
- av.add('uid').value = 'test1'
|
|
|
|
|
- av.add('dtstamp').value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
|
|
|
|
|
- av.add('dtstart').value = datetime.datetime(2006, 2, 16, 9, tzinfo=utc)
|
|
|
|
|
- av.add('dtend').value = datetime.datetime(2006, 2, 16, 12, tzinfo=utc)
|
|
|
|
|
- av.add('summary').value = "Available in the morning"
|
|
|
|
|
-
|
|
|
|
|
- vcal.add(av)
|
|
|
|
|
-
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- vcal.serialize().replace('\r\n', '\n'),
|
|
|
|
|
- test_cal.replace('\r\n', '\n')
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_recurrence(self):
|
|
|
|
|
- """
|
|
|
|
|
- Ensure date valued UNTILs in rrules are in a reasonable timezone,
|
|
|
|
|
- and include that day (12/28 in this test)
|
|
|
|
|
- """
|
|
|
|
|
- test_file = get_test_file("recurrence.ics")
|
|
|
|
|
- cal = base.readOne(test_file)
|
|
|
|
|
- dates = list(cal.vevent.getrruleset())
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- dates[0],
|
|
|
|
|
- datetime.datetime(2006, 1, 26, 23, 0, tzinfo=tzutc())
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- dates[1],
|
|
|
|
|
- datetime.datetime(2006, 2, 23, 23, 0, tzinfo=tzutc())
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- dates[-1],
|
|
|
|
|
- datetime.datetime(2006, 12, 28, 23, 0, tzinfo=tzutc())
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_recurring_component(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test recurring events
|
|
|
|
|
- """
|
|
|
|
|
- vevent = RecurringComponent(name='VEVENT')
|
|
|
|
|
-
|
|
|
|
|
- # init
|
|
|
|
|
- self.assertTrue(vevent.isNative)
|
|
|
|
|
-
|
|
|
|
|
- # rruleset should be None at this point.
|
|
|
|
|
- # No rules have been passed or created.
|
|
|
|
|
- self.assertEqual(vevent.rruleset, None)
|
|
|
|
|
-
|
|
|
|
|
- # Now add start and rule for recurring event
|
|
|
|
|
- vevent.add('dtstart').value = datetime.datetime(2005, 1, 19, 9)
|
|
|
|
|
- vevent.add('rrule').value =u"FREQ=WEEKLY;COUNT=2;INTERVAL=2;BYDAY=TU,TH"
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- list(vevent.rruleset),
|
|
|
|
|
- [datetime.datetime(2005, 1, 20, 9, 0), datetime.datetime(2005, 2, 1, 9, 0)]
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- list(vevent.getrruleset(addRDate=True)),
|
|
|
|
|
- [datetime.datetime(2005, 1, 19, 9, 0), datetime.datetime(2005, 1, 20, 9, 0)]
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- # Also note that dateutil will expand all-day events (datetime.date values)
|
|
|
|
|
- # to datetime.datetime value with time 0 and no timezone.
|
|
|
|
|
- vevent.dtstart.value = datetime.date(2005,3,18)
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- list(vevent.rruleset),
|
|
|
|
|
- [datetime.datetime(2005, 3, 29, 0, 0), datetime.datetime(2005, 3, 31, 0, 0)]
|
|
|
|
|
- )
|
|
|
|
|
- self.assertEqual(
|
|
|
|
|
- list(vevent.getrruleset(True)),
|
|
|
|
|
- [datetime.datetime(2005, 3, 18, 0, 0), datetime.datetime(2005, 3, 29, 0, 0)]
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- def test_recurrence_without_tz(self):
|
|
|
|
|
- """
|
|
|
|
|
- Test recurring vevent missing any time zone definitions.
|
|
|
|
|
- """
|
|
|
|
|
- test_file = get_test_file("recurrence-without-tz.ics")
|
|
|
|
|
- cal = base.readOne(test_file)
|
|
|
|
|
- dates = list(cal.vevent.getrruleset())
|
|
|
|
|
- self.assertEqual(dates[0], datetime.datetime(2013, 1, 17, 0, 0))
|
|
|
|
|
- self.assertEqual(dates[1], datetime.datetime(2013, 1, 24, 0, 0))
|
|
|
|
|
- self.assertEqual(dates[-1], datetime.datetime(2013, 3, 28, 0, 0))
|
|
|
|
|
-
|
|
|
|
|
- def test_recurrence_offset_naive(self):
|
|
|
|
|
- """
|
|
|
|
|
- Ensure recurring vevent missing some time zone definitions is
|
|
|
|
|
- parsing. See isseu #75.
|
|
|
|
|
- """
|
|
|
|
|
- test_file = get_test_file("recurrence-offset-naive.ics")
|
|
|
|
|
- cal = base.readOne(test_file)
|
|
|
|
|
- dates = list(cal.vevent.getrruleset())
|
|
|
|
|
- self.assertEqual(dates[0], datetime.datetime(2013, 1, 17, 0, 0))
|
|
|
|
|
- self.assertEqual(dates[1], datetime.datetime(2013, 1, 24, 0, 0))
|
|
|
|
|
- self.assertEqual(dates[-1], datetime.datetime(2013, 3, 28, 0, 0))
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-class TestChangeTZ(unittest.TestCase):
|
|
|
|
|
- """
|
|
|
|
|
- Tests for change_tz.change_tz
|
|
|
|
|
- """
|
|
|
|
|
- class StubCal(object):
|
|
|
|
|
- class StubEvent(object):
|
|
|
|
|
- class Node(object):
|
|
|
|
|
- def __init__(self, value):
|
|
|
|
|
- self.value = value
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, dtstart, dtend):
|
|
|
|
|
- self.dtstart = self.Node(dtstart)
|
|
|
|
|
- self.dtend = self.Node(dtend)
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, dates):
|
|
|
|
|
- """
|
|
|
|
|
- dates is a list of tuples (dtstart, dtend)
|
|
|
|
|
- """
|
|
|
|
|
- self.vevent_list = [self.StubEvent(*d) for d in dates]
|
|
|
|
|
-
|
|
|
|
|
- def test_change_tz(self):
|
|
|
|
|
- """
|
|
|
|
|
- Change the timezones of events in a component to a different
|
|
|
|
|
- timezone
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- # Setup - create a stub vevent list
|
|
|
|
|
- old_tz = dateutil.tz.gettz('UTC') # 0:00
|
|
|
|
|
- new_tz = dateutil.tz.gettz('America/Chicago') # -5:00
|
|
|
|
|
-
|
|
|
|
|
- dates = [
|
|
|
|
|
- (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=old_tz),
|
|
|
|
|
- datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=old_tz)),
|
|
|
|
|
- (datetime.datetime(2010, 12, 31, 23, 59, 59, 0, tzinfo=old_tz),
|
|
|
|
|
- datetime.datetime(2011, 1, 2, 3, 0, 0, 0, tzinfo=old_tz))]
|
|
|
|
|
-
|
|
|
|
|
- cal = self.StubCal(dates)
|
|
|
|
|
-
|
|
|
|
|
- # Exercise - change the timezone
|
|
|
|
|
- change_tz(cal, new_tz, dateutil.tz.gettz('UTC'))
|
|
|
|
|
-
|
|
|
|
|
- # Test - that the tzs were converted correctly
|
|
|
|
|
- expected_new_dates = [
|
|
|
|
|
- (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
|
|
|
|
|
- datetime.datetime(1999, 12, 31, 18, 0, 0, 0, tzinfo=new_tz)),
|
|
|
|
|
- (datetime.datetime(2010, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
|
|
|
|
|
- datetime.datetime(2011, 1, 1, 21, 0, 0, 0, tzinfo=new_tz))]
|
|
|
|
|
-
|
|
|
|
|
- for vevent, expected_datepair in zip(cal.vevent_list,
|
|
|
|
|
- expected_new_dates):
|
|
|
|
|
- self.assertEqual(vevent.dtstart.value, expected_datepair[0])
|
|
|
|
|
- self.assertEqual(vevent.dtend.value, expected_datepair[1])
|
|
|
|
|
-
|
|
|
|
|
- def test_change_tz_utc_only(self):
|
|
|
|
|
- """
|
|
|
|
|
- Change any UTC timezones of events in a component to a different
|
|
|
|
|
- timezone
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- # Setup - create a stub vevent list
|
|
|
|
|
- utc_tz = dateutil.tz.gettz('UTC') # 0:00
|
|
|
|
|
- non_utc_tz = dateutil.tz.gettz('America/Santiago') # -4:00
|
|
|
|
|
- new_tz = dateutil.tz.gettz('America/Chicago') # -5:00
|
|
|
|
|
-
|
|
|
|
|
- dates = [
|
|
|
|
|
- (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=utc_tz),
|
|
|
|
|
- datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=non_utc_tz))]
|
|
|
|
|
-
|
|
|
|
|
- cal = self.StubCal(dates)
|
|
|
|
|
-
|
|
|
|
|
- # Exercise - change the timezone passing utc_only=True
|
|
|
|
|
- change_tz(cal, new_tz, dateutil.tz.gettz('UTC'), utc_only=True)
|
|
|
|
|
-
|
|
|
|
|
- # Test - that only the utc item has changed
|
|
|
|
|
- expected_new_dates = [
|
|
|
|
|
- (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
|
|
|
|
|
- dates[0][1])]
|
|
|
|
|
-
|
|
|
|
|
- for vevent, expected_datepair in zip(cal.vevent_list,
|
|
|
|
|
- expected_new_dates):
|
|
|
|
|
- self.assertEqual(vevent.dtstart.value, expected_datepair[0])
|
|
|
|
|
- self.assertEqual(vevent.dtend.value, expected_datepair[1])
|
|
|
|
|
-
|
|
|
|
|
- def test_change_tz_default(self):
|
|
|
|
|
- """
|
|
|
|
|
- Change the timezones of events in a component to a different
|
|
|
|
|
- timezone, passing a default timezone that is assumed when the events
|
|
|
|
|
- don't have one
|
|
|
|
|
- """
|
|
|
|
|
-
|
|
|
|
|
- # Setup - create a stub vevent list
|
|
|
|
|
- new_tz = dateutil.tz.gettz('America/Chicago') # -5:00
|
|
|
|
|
-
|
|
|
|
|
- dates = [
|
|
|
|
|
- (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=None),
|
|
|
|
|
- datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=None))]
|
|
|
|
|
-
|
|
|
|
|
- cal = self.StubCal(dates)
|
|
|
|
|
-
|
|
|
|
|
- # Exercise - change the timezone
|
|
|
|
|
- change_tz(cal, new_tz, dateutil.tz.gettz('UTC'))
|
|
|
|
|
-
|
|
|
|
|
- # Test - that the tzs were converted correctly
|
|
|
|
|
- expected_new_dates = [
|
|
|
|
|
- (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
|
|
|
|
|
- datetime.datetime(1999, 12, 31, 18, 0, 0, 0, tzinfo=new_tz))]
|
|
|
|
|
-
|
|
|
|
|
- for vevent, expected_datepair in zip(cal.vevent_list,
|
|
|
|
|
- expected_new_dates):
|
|
|
|
|
- self.assertEqual(vevent.dtstart.value, expected_datepair[0])
|
|
|
|
|
- self.assertEqual(vevent.dtend.value, expected_datepair[1])
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-if __name__ == '__main__':
|
|
|
|
|
- unittest.main()
|
|
|