diff --git a/.coveragerc b/.coveragerc index 03ff52f..da61172 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,7 @@ [run] branch = True -source = parsedatetime +include = + parsedatetime/* + tests/lib/* +omit = + tests/*/test_* diff --git a/.editorconfig b/.editorconfig index 7b3eacb..5e855b8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,3 +5,7 @@ root = true [*.py] indent_style = space indent_size = 4 + +[*.yml] +indent_style = space +indent_size = 4 diff --git a/Makefile b/Makefile index 13fd32f..b11e3f4 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ tox: clean tox coverage: clean - @coverage run --source=parsedatetime setup.py test + @coverage run setup.py test @coverage html @coverage report diff --git a/parsedatetime/context.py b/parsedatetime/context.py index c1cc39a..9b42342 100644 --- a/parsedatetime/context.py +++ b/parsedatetime/context.py @@ -73,6 +73,7 @@ class pdtContext(object): ACU_MIN = 2 ** 6 ACU_SEC = 2 ** 7 ACU_NOW = 2 ** 8 + ACU_WILDCARD = 2 ** 9 ACU_DATE = ACU_YEAR | ACU_MONTH | ACU_WEEK | ACU_DAY ACU_TIME = ACU_HALFDAY | ACU_HOUR | ACU_MIN | ACU_SEC | ACU_NOW @@ -116,6 +117,15 @@ class pdtContext(object): 'seconds': ACU_SEC, 'now': ACU_NOW} + @classmethod + def fromAccuracyStrings(cls, accuracyStrings): + accuracy = 0 + for accuracyString in accuracyStrings: + if accuracyString in cls._ACCURACY_REVERSE_MAPPING: + accuracy |= cls._ACCURACY_REVERSE_MAPPING[accuracyString] + + return cls(accuracy) + def __init__(self, accuracy=0): """ Default constructor of L{pdtContext} class. @@ -184,4 +194,10 @@ def __repr__(self): return 'pdtContext(%s)' % accuracy_repr def __eq__(self, ctx): - return self.accuracy == ctx.accuracy + if not isinstance(ctx, pdtContext): + return False + return ( + self.accuracy == ctx.accuracy or + self.accuracy == self.ACU_WILDCARD or + ctx.accuracy == self.ACU_WILDCARD + ) diff --git a/pytest.ini b/pytest.ini index f60750e..7ab640e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,7 +1,8 @@ [pytest] norecursedirs = venv .tox +testpaths = tests verbosity = 2 -python_files=Test*.py +python_files=test_*.py with-coverage = true cover-min-percentage = 72 diff --git a/requirements.testing.txt b/requirements.testing.txt index 666b599..ddc4008 100644 --- a/requirements.testing.txt +++ b/requirements.testing.txt @@ -1,12 +1,13 @@ pytest pytest-cov +pytest-mock pytest-runner +pyyaml mccabe flake8 coverage coveralls codecov check-manifest -unittest2 tox tox-pyenv \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 957d9cd..1a59fca 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,4 +4,7 @@ ignore = violations.flake8.txt [flake8] -ignore = E111,E124,E126,E201,E202,E221,E241,E302,E501 \ No newline at end of file +ignore = E111,E124,E126,E201,E202,E221,E241,E302,E501 + +[aliases] +test=pytest \ No newline at end of file diff --git a/tests/TestAlternativeAbbreviations.py b/tests/TestAlternativeAbbreviations.py deleted file mode 100644 index 6aa874b..0000000 --- a/tests/TestAlternativeAbbreviations.py +++ /dev/null @@ -1,130 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -import sys -import time -import datetime -import parsedatetime as pdt -from parsedatetime.pdt_locales import get_icu -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -pdtLocale_en = get_icu('en_US') -pdtLocale_en.Weekdays = [ - 'monday', 'tuesday', 'wednesday', - 'thursday', 'friday', 'saturday', 'sunday'] - -pdtLocale_en.shortWeekdays = [ - 'mon|mond', 'tue|tues', 'wed|wedn', - 'thu|thur|thurs', 'fri|frid', 'sat|sa', 'sun|su'] - -pdtLocale_en.Months = [ - 'january', 'february', 'march', 'april', 'may', 'june', - 'july', 'august', 'september', 'october', 'november', 'december'] - -pdtLocale_en.shortMonths = [ - 'jan|janu', 'feb|febr', 'mar|marc', 'apr|apri', 'may', 'jun|june', - 'jul', 'aug|augu', 'sep|sept', 'oct|octo', 'nov|novem', 'dec|decem'] - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - pdt.pdtLocales['en_us'] = pdtLocale_en # override for the test - self.ptc = pdt.Constants('en_us', usePyICU=False) - self.cal = pdt.Calendar(self.ptc) - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testDaysOfWeek(self): - start = datetime.datetime( - 2014, 10, 25, self.hr, self.mn, self.sec).timetuple() - - target = datetime.datetime( - 2014, 10, 26, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult(self.cal.parse('sunday', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('sun', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('su', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 27, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult(self.cal.parse('Monday', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('mon', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('mond', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 28, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('tuesday', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('tues', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('tue', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 29, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('wednesday', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('wedn', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('wed', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 30, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('thursday', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('thu', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('thur', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('thurs', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 31, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult(self.cal.parse('friday', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('fri', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('frid', start), (target, 1)) - - target = datetime.datetime( - 2014, 11, 1, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('saturday', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('sat', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('sa', start), (target, 1)) - - def testMonths(self): - start = datetime.datetime( - 2014, 1, 1, self.hr, self.mn, self.sec).timetuple() - for dates, expected_date in [ - ('jan|janu|january', datetime.datetime( - 2014, 1, 1, self.hr, self.mn, self.sec).timetuple()), - ('feb|febr|february', datetime.datetime( - 2014, 2, 1, self.hr, self.mn, self.sec).timetuple()), - ('mar|marc|march', datetime.datetime( - 2014, 3, 1, self.hr, self.mn, self.sec).timetuple()), - ('apr|apri|april', datetime.datetime( - 2014, 4, 1, self.hr, self.mn, self.sec).timetuple()), - ('may|may', datetime.datetime( - 2014, 5, 1, self.hr, self.mn, self.sec).timetuple()), - ('jun|june', datetime.datetime( - 2014, 6, 1, self.hr, self.mn, self.sec).timetuple()), - ('jul|july', datetime.datetime( - 2014, 7, 1, self.hr, self.mn, self.sec).timetuple()), - ('aug|augu|august', datetime.datetime( - 2014, 8, 1, self.hr, self.mn, self.sec).timetuple()), - ('sep|sept|september', datetime.datetime( - 2014, 9, 1, self.hr, self.mn, self.sec).timetuple()), - ('oct|octo|october', datetime.datetime( - 2014, 10, 1, self.hr, self.mn, self.sec).timetuple()), - ('nov|novem|november', datetime.datetime( - 2014, 11, 1, self.hr, self.mn, self.sec).timetuple()), - ('dec|decem|december', datetime.datetime( - 2014, 12, 1, self.hr, self.mn, self.sec).timetuple()) - ]: - for dateText in dates.split("|"): - # print dateText - self.assertExpectedResult( - self.cal.parse(dateText, start), (expected_date, 1)) diff --git a/tests/TestAustralianLocale.py b/tests/TestAustralianLocale.py deleted file mode 100644 index 3aeb3a0..0000000 --- a/tests/TestAustralianLocale.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of simple date and times using the Australian locale -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.ptc = pdt.Constants('en_AU', usePyICU=False) - self.cal = pdt.Calendar(self.ptc) - - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - if self.ptc.localeID != 'en_AU': - raise unittest.SkipTest( - 'Locale not set to en_AU - check if PyICU is installed') - - def testTimes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 23, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('11:00:00 PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00 PM', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11 PM', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11PM', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('2300', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('23:00', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11p', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11pm', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 11, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('11:00:00 AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00 AM', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11 AM', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11AM', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('1100', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11:00', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11a', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11am', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 7, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('0730', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('1730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('173000', start), (target, 2)) - - def testDates(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - 2006, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('25-08-2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25/08/2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25.08.2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25-8-06', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25/8/06', start), (target, 1)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 25): - target = datetime.datetime( - self.yr + 1, 8, 25, self.hr, self.mn, self.sec).timetuple() - else: - target = datetime.datetime( - self.yr, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult(self.cal.parse('25-8', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('25/8', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('25.8', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('25-08', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('25/08', start), (target, 1)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestComplexDateTimes.py b/tests/TestComplexDateTimes.py deleted file mode 100644 index a1afd1f..0000000 --- a/tests/TestComplexDateTimes.py +++ /dev/null @@ -1,164 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of complex date and times -""" -from __future__ import unicode_literals - -import sys -import time -from datetime import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testDate3ConfusedHourAndYear(self): - start = datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('Aug 05, 2014 4:15 AM'), - (datetime(2014, 8, 5, 4, 15, 0).timetuple(), 3)) - self.assertExpectedResult( - self.cal.parse('Aug 05, 2003 3:15 AM'), - (datetime(2003, 8, 5, 3, 15, 0).timetuple(), 3)) - self.assertExpectedResult( - self.cal.parse('Aug 05, 2003 03:15 AM'), - (datetime(2003, 8, 5, 3, 15, 0).timetuple(), 3)) - self.assertExpectedResult( - self.cal.parse('June 30th 12PM', start), - (datetime(self.yr - if datetime.now() < datetime(self.yr, 6, 30, 12) - else self.yr + 1, - 6, 30, 12, 0, 0).timetuple(), 3)) - self.assertExpectedResult( - self.cal.parse('June 30th 12:00', start), - (datetime(self.yr - if datetime.now() < datetime(self.yr, 6, 30, 12) - else self.yr + 1, - 6, 30, 12, 0, 0).timetuple(), 3)) - self.assertExpectedResult( - self.cal.parse('December 30th 23PM', start), - (datetime(self.yr - if datetime.now() < datetime(self.yr, 12, 30, 23) - else self.yr + 1, - 12, 30, 23, 0, 0).timetuple(), 3)) - self.assertExpectedResult( - self.cal.parse('December 30th 23:02', start), - (datetime(self.yr - if datetime.now() < datetime(self.yr, 12, 30, 23, 2) - else self.yr + 1, - 12, 30, 23, 2, 0).timetuple(), 3)) - - def testDates(self): - start = datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime(2006, 8, 25, 17, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('08/25/2006 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5pm on 08.25.2006', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5pm August 25, 2006', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5pm August 25th, 2006', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5pm 25 August, 2006', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5pm 25th August, 2006', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('Aug 25, 2006 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('Aug 25th, 2006 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('25 Aug, 2006 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('25th Aug 2006, 5pm', start), (target, 3)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 5): - target = datetime(self.yr + 1, 8, 5, 17, 0, 0).timetuple() - else: - target = datetime(self.yr, 8, 5, 17, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('8/5 at 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5pm 8.5', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('08/05 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('August 5 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5pm Aug 05', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('Aug 05 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('Aug 05th 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5 August 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5th August 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('5pm 05 Aug', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('05 Aug 5pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('05th Aug 5pm', start), (target, 3)) - - self.assertExpectedResult( - self.cal.parse('August 5th 5pm', start), (target, 3)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 5): - target = datetime(self.yr + 1, 8, 5, 12, 0, 0).timetuple() - else: - target = datetime(self.yr, 8, 5, 12, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('August 5th 12:00', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('August 5th 12pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('August 5th 12:00pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('August 5th 12 pm', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('August 5th 12:00 pm', start), (target, 3)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 22): - target = datetime( - self.yr + 1, 8, 22, 3, 26, 0).timetuple() - else: - target = datetime(self.yr, 8, 22, 3, 26, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('August 22nd 3:26', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('August 22nd 3:26am', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('August 22nd 3:26 am', start), (target, 3)) - - def testDatesWithDay(self): - start = datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime(2016, 8, 23, 17, 0, 0).timetuple() - self.assertExpectedResult( - self.cal.parse('tuesday august 23nd 2016 at 5pm', start), (target, 3)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestContext.py b/tests/TestContext.py deleted file mode 100644 index c3ab616..0000000 --- a/tests/TestContext.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test pdtContext -""" - -import sys -import time -import parsedatetime as pdt -from parsedatetime.context import pdtContext - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - def setUp(self): - self.cal = pdt.Calendar(version=pdt.VERSION_CONTEXT_STYLE) - (self.yr, self.mth, self.dy, self.hr, self.mn, - self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testContext(self): - self.assertEqual(self.cal.parse('5 min from now')[1], - pdtContext(pdtContext.ACU_MIN | pdtContext.ACU_NOW)) - self.assertEqual(self.cal.parse('5 min from now', - version=pdt.VERSION_FLAG_STYLE)[1], 2) - self.assertEqual(self.cal.parse('7/11/2015')[1], - pdtContext(pdtContext.ACU_YEAR | - pdtContext.ACU_MONTH | pdtContext.ACU_DAY)) - self.assertEqual(self.cal.parse('7/11/2015', - version=pdt.VERSION_FLAG_STYLE)[1], 1) - self.assertEqual(self.cal.parse('14/32/2015')[1], - pdtContext(0)) - self.assertEqual(self.cal.parse('25:23')[1], - pdtContext()) - - def testSources(self): - self.assertEqual(self.cal.parse('afternoon 5pm')[1], - pdtContext(pdtContext.ACU_HALFDAY | - pdtContext.ACU_HOUR)) - - self.assertEqual(self.cal.parse('morning')[1], - pdtContext(pdtContext.ACU_HALFDAY)) - - self.assertEqual(self.cal.parse('night', version=1)[1], 2) - - def testThreadRun(self): - from threading import Thread - t = Thread(target=lambda: self.cal.evalRanges('4p-6p')) - # should not throw out AttributeError - t.start() - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestConvertUnitAsWords.py b/tests/TestConvertUnitAsWords.py deleted file mode 100644 index ba15809..0000000 --- a/tests/TestConvertUnitAsWords.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -Tests the _convertUnitAsWords method. -""" - -import sys -import parsedatetime as pdt - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - def setUp(self): - self.cal = pdt.Calendar() - self.tests = (('one', 1), - ('zero', 0), - ('eleven', 11), - ('forty two', 42), - ('a hundred', 100), - ('four hundred and fifteen', 415), - ('twelve thousand twenty', 12020), - ('nine hundred and ninety nine', 999), - ('three quintillion four billion', 3000000004000000000), - ('forty three thousand, nine hundred and ninety nine', 43999), - ('one hundred thirty three billion four hundred thousand three hundred fourteen', 133000400314), - ('an octillion', 1000000000000000000000000000) - ) - - def testConversions(self): - for pair in self.tests: - self.assertEqual(self.cal._convertUnitAsWords(pair[0]), pair[1]) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestDayStartHour.py b/tests/TestDayStartHour.py deleted file mode 100644 index 9876582..0000000 --- a/tests/TestDayStartHour.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of strings that are phrases -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - # Test with a different day start hour. - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testDifferentDayStartHours(self): - for day_start_hour in (0, 6, 9, 12): - cal = pdt.Calendar( - day_start_hour=day_start_hour) - - s = datetime.datetime.now() - t = datetime.datetime( - self.yr, self.mth, self.dy, - day_start_hour, 0, 0) + datetime.timedelta(days=1) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - cal.parse('tomorrow', start), (target, 1)) - self.assertExpectedResult( - cal.parse('next day', start), (target, 1)) - - t = datetime.datetime( - self.yr, self.mth, self.dy, - day_start_hour, 0, 0) + datetime.timedelta(days=-1) - target = t.timetuple() - - self.assertExpectedResult( - cal.parse('yesterday', start), (target, 1)) - - t = datetime.datetime( - self.yr, self.mth, self.dy, - day_start_hour, 0, 0) - target = t.timetuple() - - self.assertExpectedResult(cal.parse('today', start), (target, 1)) diff --git a/tests/TestDelta.py b/tests/TestDelta.py deleted file mode 100644 index 107b39d..0000000 --- a/tests/TestDelta.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test time delta -""" - -import sys -import time -import datetime -import parsedatetime as pdt - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -# added to support Python 2.6 which does not have total_seconds() method for timedelta -def total_seconds(timedelta): - return (timedelta.microseconds + 0.0 + - (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6 - - -class test(unittest.TestCase): - - def setUp(self): - self.cal = pdt.Calendar(version=pdt.VERSION_CONTEXT_STYLE) - self.source = (self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec, - self.wd, self.yd, self.isdst) = time.localtime() - - def assertDelta(self, ts, years=None, months=None, **deltakw): - ts = ts[0] - source = self.source - delta = datetime.timedelta(**deltakw) - calc_delta = (datetime.datetime(*ts[:6]) - - datetime.datetime(*source[:6])) - delta -= datetime.timedelta(microseconds=delta.microseconds) - if not years and not months: - self.assertEqual(delta, calc_delta) - return - if years: - delta += datetime.timedelta(days=365 * years) - if months: - delta += datetime.timedelta(days=30 * months) - diff = abs((total_seconds(calc_delta) - - total_seconds(delta)) / - total_seconds(delta)) - self.assertTrue(diff < 0.05, '%s is not less than 0.05' % diff) - - def testInteger(self): - self.assertDelta( - self.cal.parse('5 minutes ago', self.source), minutes=-5) - self.assertDelta( - self.cal.parse('34 hours ago', self.source), hours=-34) - self.assertDelta( - self.cal.parse('2 days ago', self.source), days=-2) - - def testFloat(self): - self.assertDelta( - self.cal.parse('58.4 minutes ago', self.source), minutes=-58.4) - self.assertDelta( - self.cal.parse('1855336.424 minutes ago', self.source), - minutes=-1855336.424) - self.assertDelta( - self.cal.parse('8.3 hours ago', self.source), hours=-8.3) - self.assertDelta( - self.cal.parse('22.355 hours ago', self.source), hours=-22.355) - self.assertDelta( - self.cal.parse('7.2 days ago', self.source), days=-7.2) - self.assertDelta( - self.cal.parse('7.3 days ago', self.source), days=-7.3) - self.assertDelta( - self.cal.parse('17.7 days ago', self.source), days=-17.7) - self.assertDelta( - self.cal.parse('1.4 months ago', self.source), months=-1.4) - self.assertDelta( - self.cal.parse('4.8 months ago', self.source), months=-4.8) - self.assertDelta( - self.cal.parse('5.1 months ago', self.source), months=-5.1) - self.assertDelta( - self.cal.parse('5.11553 years ago', self.source), years=-5.11553) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestErrors.py b/tests/TestErrors.py deleted file mode 100644 index 8345450..0000000 --- a/tests/TestErrors.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of units -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - @utils.assertEqualWithComparator - def assertExpectedErrorFlag(self, result, check, **kwargs): - return utils.compareResultByFlags(result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testErrors(self): - s = datetime.datetime.now() - start = s.timetuple() - - # These tests all return current date/time as they are out of range - self.assertExpectedResult(self.cal.parse('01/0', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('08/35', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('18/35', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('1799', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('781', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('2702', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('78', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('11', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('1', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('174565', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('177505', start), (start, 0)) - # ensure short month names do not cause false positives within a word - - # jun (june) - self.assertExpectedResult( - self.cal.parse('injunction', start), (start, 0)) - # ensure short month names do not cause false positives at the start of - # a word - jul (juuly) - self.assertExpectedResult(self.cal.parse('julius', start), (start, 0)) - # ensure short month names do not cause false positives at the end of a - # word - mar (march) - self.assertExpectedResult(self.cal.parse('lamar', start), (start, 0)) - # ensure short weekday names do not cause false positives within a word - # - mon (monday) - self.assertExpectedResult( - self.cal.parse('demonize', start), (start, 0)) - # ensure short weekday names do not cause false positives at the start - # of a word - mon (monday) - self.assertExpectedResult(self.cal.parse('money', start), (start, 0)) - # ensure short weekday names do not cause false positives at the end of - # a word - th (thursday) - self.assertExpectedResult(self.cal.parse('month', start), (start, 0)) - self.assertExpectedErrorFlag( - self.cal.parse('30/030/01/071/07', start), (start, 0)) - # overflow due to Python's datetime - self.assertExpectedResult(self.cal.parse('12345 y', start), (start, 0)) - self.assertExpectedResult( - self.cal.parse('654321 w', start), (start, 0)) - self.assertExpectedResult( - self.cal.parse('3700000 d', start), (start, 0)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestFrenchLocale.py b/tests/TestFrenchLocale.py deleted file mode 100644 index b37f13f..0000000 --- a/tests/TestFrenchLocale.py +++ /dev/null @@ -1,172 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of simple date and times using the French locale - -Note: requires PyICU -""" -from __future__ import unicode_literals -import sys -import time -import datetime -import parsedatetime as pdt -from parsedatetime.pdt_locales import get_icu -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.ptc = pdt.Constants('fr_FR', usePyICU=True) - self.cal = pdt.Calendar(self.ptc) - - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - if self.ptc.localeID != 'fr_FR': - raise unittest.SkipTest( - 'Locale not set to fr_FR - check if PyICU is installed') - - def testTimes(self): - if self.ptc.localeID == 'fr_FR': - start = datetime.datetime( - self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 23, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('2300', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('23:00', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 11, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('1100', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 7, 30, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('730', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('0730', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 30, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('1730', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('173000', start), (target, 2)) - - def testDates(self): - if self.ptc.localeID == 'fr_FR': - start = datetime.datetime( - self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - 2006, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('25/08/2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25/8/06', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('ao\xfbt 25, 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('ao\xfbt 25 2006', start), (target, 1)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 25): - target = datetime.datetime( - self.yr + 1, 8, 25, self.hr, self.mn, self.sec).timetuple() - else: - target = datetime.datetime( - self.yr, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('25/8', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25/08', start), (target, 1)) - - def testWeekDays(self): - if self.ptc.localeID == 'fr_FR': - start = datetime.datetime( - self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec).timetuple() - - o1 = self.ptc.CurrentDOWParseStyle - o2 = self.ptc.DOWParseStyle - - # set it up so the current dow returns current day - self.ptc.CurrentDOWParseStyle = True - self.ptc.DOWParseStyle = 1 - - for i in range(0, 7): - dow = self.ptc.shortWeekdays[i] - - result = self.cal.parse(dow, start) - - yr, mth, dy, hr, mn, sec, wd, yd, isdst = result[0] - - self.assertEqual(wd, i) - - self.ptc.CurrentDOWParseStyle = o1 - self.ptc.DOWParseStyle = o2 - - -pdtLocale_fr = get_icu('fr_FR') -pdtLocale_fr.dayOffsets.update({"aujourd'hui": 0, 'demain': 1, 'hier': -1}) - - -class TestDayOffsets(test): - # test how Aujourd'hui/Demain/Hier are parsed - - def setUp(self): - super(TestDayOffsets, self).setUp() - self.__old_pdtlocale_fr = pdt.pdtLocales.get('fr_FR') # save for later - pdt.pdtLocales['fr_FR'] = pdtLocale_fr # override for the test - self.ptc = pdt.Constants('fr_FR', usePyICU=False) - self.cal = pdt.Calendar(self.ptc) - - def test_dayoffsets(self): - start = datetime.datetime(self.yr, self.mth, self.dy, 9) - for date_string, expected_day_offset in [ - ("Aujourd'hui", 0), - ("aujourd'hui", 0), - ("Demain", 1), - ("demain", 1), - ("Hier", -1), - ("hier", -1), - ("today", 0), # assume default names exist - ("tomorrow", 1), - ("yesterday", -1), - ("au jour de hui", None)]: - got_dt, rc = self.cal.parseDT(date_string, start) - if expected_day_offset is not None: - self.assertEqual(rc, 1) - target = (start + datetime.timedelta(days=expected_day_offset)) - self.assertEqual(got_dt, target) - else: - self.assertEqual(rc, 0) - - def tearDown(self): - if self.__old_pdtlocale_fr is not None: # restore the locale - pdt.pdtLocales['fr_FR'] = self.__old_pdtlocale_fr - super(TestDayOffsets, self).tearDown() - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestGermanLocale.py b/tests/TestGermanLocale.py deleted file mode 100644 index 3dac4f9..0000000 --- a/tests/TestGermanLocale.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of simple date and times using the German locale -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.ptc = pdt.Constants('de_DE', usePyICU=False) - self.cal = pdt.Calendar(self.ptc) - - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - if self.ptc.localeID != 'de_DE': - raise unittest.SkipTest( - 'Locale not set to de_DE - check if PyICU is installed') - - def testTimes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 23, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('23:00:00', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('23:00', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('2300', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 11, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('11:00:00', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1100', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 7, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('0730', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('1730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('173000', start), (target, 2)) - - def testDates(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - 2006, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('25.08.2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25.8.06', start), (target, 1)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 25): - target = datetime.datetime( - self.yr + 1, 8, 25, self.hr, self.mn, self.sec).timetuple() - else: - target = datetime.datetime( - self.yr, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('25.8', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25.08', start), (target, 1)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestInc.py b/tests/TestInc.py deleted file mode 100644 index 25cd8ae..0000000 --- a/tests/TestInc.py +++ /dev/null @@ -1,113 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test Calendar.Inc() routine -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags((result, 1), - (check, 1), **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testIncMonths(self): - s = datetime.datetime(2006, 1, 1, 12, 0, 0) - t = datetime.datetime(2006, 2, 1, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, month=1).timetuple(), t.timetuple()) - - s = datetime.datetime(2006, 12, 1, 12, 0, 0) - t = datetime.datetime(2007, 1, 1, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, month=1).timetuple(), t.timetuple()) - - # leap year, Feb 1 - s = datetime.datetime(2008, 2, 1, 12, 0, 0) - t = datetime.datetime(2008, 3, 1, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, month=1).timetuple(), t.timetuple()) - - # leap year, Feb 29 - s = datetime.datetime(2008, 2, 29, 12, 0, 0) - t = datetime.datetime(2008, 3, 29, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, month=1).timetuple(), t.timetuple()) - - s = datetime.datetime(2006, 1, 1, 12, 0, 0) - t = datetime.datetime(2005, 12, 1, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, month=-1).timetuple(), t.timetuple()) - - # End of month Jan 31 to Feb - Febuary only has 28 days - s = datetime.datetime(2006, 1, 31, 12, 0, 0) - t = datetime.datetime(2006, 2, 28, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, month=1).timetuple(), t.timetuple()) - - # walk thru months and make sure month increment doesn't set the day - # to be past the last day of the new month - # think Jan transition to Feb - 31 days to 28 days - for m in range(1, 11): - d = self.cal.ptc.daysInMonth(m, 2006) - s = datetime.datetime(2006, m, d, 12, 0, 0) - - if d > self.cal.ptc.daysInMonth(m + 1, 2006): - d = self.cal.ptc.daysInMonth(m + 1, 2006) - - t = datetime.datetime(2006, m + 1, d, 12, 0, 0) - - self.assertExpectedResult( - self.cal.inc(s, month=1).timetuple(), t.timetuple()) - - def testIncYears(self): - s = datetime.datetime(2006, 1, 1, 12, 0, 0) - t = datetime.datetime(2007, 1, 1, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, year=1).timetuple(), t.timetuple()) - - s = datetime.datetime(2006, 1, 1, 12, 0, 0) - t = datetime.datetime(2008, 1, 1, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, year=2).timetuple(), t.timetuple()) - - s = datetime.datetime(2006, 12, 31, 12, 0, 0) - t = datetime.datetime(2007, 12, 31, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, year=1).timetuple(), t.timetuple()) - - s = datetime.datetime(2006, 12, 31, 12, 0, 0) - t = datetime.datetime(2005, 12, 31, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, year=-1).timetuple(), t.timetuple()) - - s = datetime.datetime(2008, 3, 1, 12, 0, 0) - t = datetime.datetime(2009, 3, 1, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, year=1).timetuple(), t.timetuple()) - - s = datetime.datetime(2008, 3, 1, 12, 0, 0) - t = datetime.datetime(2007, 3, 1, 12, 0, 0) - self.assertExpectedResult( - self.cal.inc(s, year=-1).timetuple(), t.timetuple()) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestLocaleBase.py b/tests/TestLocaleBase.py deleted file mode 100644 index f470894..0000000 --- a/tests/TestLocaleBase.py +++ /dev/null @@ -1,178 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of simple date and times using the French locale - -Note: requires PyICU -""" -from __future__ import unicode_literals -import sys -import time -import datetime -import pytest -import parsedatetime as pdt -from parsedatetime.pdt_locales import get_icu -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -pdtLocale_fr = get_icu('fr_FR') -pdtLocale_fr.dayOffsets.update({"aujourd'hui": 0, 'demain': 1, 'hier': -1}) - - -@pytest.mark.skipif(not pdtLocale_fr, reason="French Locale not found") -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.ptc = pdt.Constants('fr_FR', usePyICU=True) - self.cal = pdt.Calendar(self.ptc) - - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - if self.ptc.localeID != 'fr_FR': - raise unittest.SkipTest( - 'Locale not set to fr_FR - check if PyICU is installed') - - def testTimes(self): - if self.ptc.localeID == 'fr_FR': - start = datetime.datetime( - self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 23, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('2300', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('23:00', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 11, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('1100', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 7, 30, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('730', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('0730', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 30, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('1730', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('173000', start), (target, 2)) - - def testDates(self): - if self.ptc.localeID == 'fr_FR': - start = datetime.datetime( - self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - 2006, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('25/08/2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25/8/06', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('août 25, 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('août 25 2006', start), (target, 1)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 25): - target = datetime.datetime( - self.yr + 1, 8, 25, self.hr, self.mn, self.sec).timetuple() - else: - target = datetime.datetime( - self.yr, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('25/8', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25/08', start), (target, 1)) - - def testWeekDays(self): - if self.ptc.localeID == 'fr_FR': - start = datetime.datetime( - self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec).timetuple() - - o1 = self.ptc.CurrentDOWParseStyle - o2 = self.ptc.DOWParseStyle - - # set it up so the current dow returns current day - self.ptc.CurrentDOWParseStyle = True - self.ptc.DOWParseStyle = 1 - - for i in range(0, 7): - dow = self.ptc.shortWeekdays[i] - - result = self.cal.parse(dow, start) - - yr, mth, dy, hr, mn, sec, wd, yd, isdst = result[0] - - self.assertEqual(wd, i) - - self.ptc.CurrentDOWParseStyle = o1 - self.ptc.DOWParseStyle = o2 - - -@pytest.mark.skipif(not pdtLocale_fr, reason="French Locale not found") -class TestDayOffsets(test): - # test how Aujourd'hui/Demain/Hier are parsed - - def setUp(self): - super(TestDayOffsets, self).setUp() - self.__old_pdtlocale_fr = pdt.pdtLocales.get('fr_FR') # save for later - pdt.pdtLocales['fr_FR'] = pdtLocale_fr # override for the test - self.ptc = pdt.Constants('fr_FR', usePyICU=False) - self.day_start_hour = 9 - self.cal = pdt.Calendar( - self.ptc, day_start_hour=self.day_start_hour) - - def test_dayoffsets(self): - start = datetime.datetime(self.yr, self.mth, self.dy, - self.day_start_hour) - for date_string, expected_day_offset in [ - ("Aujourd'hui", 0), - ("aujourd'hui", 0), - ("Demain", 1), - ("demain", 1), - ("Hier", -1), - ("hier", -1), - ("today", 0), # assume default names exist - ("tomorrow", 1), - ("yesterday", -1), - ("au jour de hui", None)]: - got_dt, rc = self.cal.parseDT(date_string, start) - if expected_day_offset is not None: - self.assertEqual(rc, 1) - target = (start + datetime.timedelta(days=expected_day_offset)) - self.assertEqual(got_dt, target) - else: - self.assertEqual(rc, 0) - - def tearDown(self): - if self.__old_pdtlocale_fr is not None: # restore the locale - pdt.pdtLocales['fr_FR'] = self.__old_pdtlocale_fr - super(TestDayOffsets, self).tearDown() - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestMultiple.py b/tests/TestMultiple.py deleted file mode 100644 index e403e02..0000000 --- a/tests/TestMultiple.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of strings with multiple chunks -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testSimpleMultipleItems(self): - s = datetime.datetime.now() - t = self.cal.inc(s, year=3) + datetime.timedelta(days=5, weeks=2) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('3 years 2 weeks 5 days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('3years 2weeks 5days', start), (target, 1)) - - def testMultipleItemsSingleCharUnits(self): - s = datetime.datetime.now() - t = self.cal.inc(s, year=3) + datetime.timedelta(days=5, weeks=2) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('3 y 2 w 5 d', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('3y 2w 5d', start), (target, 1)) - - t = self.cal.inc(s, year=3) + datetime.timedelta(hours=5, minutes=50) - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('3y 5h 50m', start), (target, 3)) - - def testMultipleItemsWithPunctuation(self): - s = datetime.datetime.now() - t = self.cal.inc(s, year=3) + datetime.timedelta(days=5, weeks=2) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('3 years, 2 weeks, 5 days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('3 years, 2 weeks and 5 days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('3y, 2w, 5d ', start), (target, 1)) - - def testUnixATStyle(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(days=3) - - t = t.replace(hour=16, minute=0, second=0) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('4pm + 3 days', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('4pm +3 days', start), (target, 3)) - - def testUnixATStyleNegative(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(days=-3) - - t = t.replace(hour=16, minute=0, second=0) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('4pm - 3 days', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('4pm -3 days', start), (target, 3)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestNlp.py b/tests/TestNlp.py deleted file mode 100644 index 9ba2abb..0000000 --- a/tests/TestNlp.py +++ /dev/null @@ -1,273 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of strings that are phrases -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - # a special compare function for nlp returned data - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, dateOnly=False): - target = result - value = check - - if target is None and value is None: - return True - - if (target is None and value is not None) or \ - (target is not None and value is None): - return False - - if len(target) != len(value): - return False - - for i in range(0, len(target)): - target_date = target[i][0] - value_date = value[i][0] - - if target_date.year != value_date.year or \ - target_date.month != value_date.month or \ - target_date.day != value_date.day or \ - target_date.hour != value_date.hour or \ - target_date.minute != value_date.minute: - return False - if target[i][1] != value[i][1]: - return False - if target[i][2] != value[i][2]: - return False - if target[i][3] != value[i][3]: - return False - if target[i][4] != value[i][4]: - return False - - return True - - def setUp(self): - self.day_start_hour = 9 - self.cal = pdt.Calendar( - day_start_hour=self.day_start_hour) - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testLongPhrase(self): - # note: these tests do not need to be as dynamic as the others because - # this is still based on the parse() function, so all tests of - # the actual processing of the datetime value returned are - # applicable to this. Here we are concerned with ensuring the - # correct portions of text and their positions are extracted and - # processed. - start = datetime.datetime(2013, 8, 1, 21, 25, 0).timetuple() - target = ((datetime.datetime(2013, 8, 5, 20, 0), - 3, 17, 37, 'At 8PM on August 5th'), - (datetime.datetime(2013, 8, 9, 21, 0), - 3, 72, 90, 'next Friday at 9PM'), - (datetime.datetime(2013, 8, 1, 21, 30, 0), - 2, 120, 132, 'in 5 minutes'), - (datetime.datetime(2013, 8, 8, self.day_start_hour, 0), - 1, 173, 182, 'next week')) - - # positive testing - self.assertExpectedResult(self.cal.nlp( - "I'm so excited!! At 8PM on August 5th i'm going to fly to " - "Florida. Then next Friday at 9PM i'm going to Dog n Bone! " - "And in 5 minutes I'm going to eat some food! Talk to you " - "next week.", start), target) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 0, 0).timetuple() - - def testQuotes(self): - # quotes should not interfere with datetime language recognition - start = datetime.datetime(2013, 8, 1, 21, 25, 0).timetuple() - target = self.cal.nlp( - "I'm so excited!! At '8PM on August 5th' i'm going to fly to " - "Florida. Then 'next Friday at 9PM' i'm going to Dog n Bone! " - "And in '5 minutes' I'm going to eat some food! Talk to you " - '"next week"', start) - - self.assertEqual(target[0][4], "At '8PM on August 5th") - self.assertEqual(target[1][4], "next Friday at 9PM") - self.assertEqual(target[2][4], "in '5 minutes") - self.assertEqual(target[3][4], "next week") - - def testPrefixes(self): - # nlp has special handling for on/in/at prefixes - start = datetime.datetime(2013, 8, 1, 21, 25, 0).timetuple() - - target = self.cal.nlp("Buy a balloon on Monday", start) - self.assertEqual(target[0][4], "on Monday") - - target = self.cal.nlp("Buy a balloon at noon", start) - self.assertEqual(target[0][4], "at noon") - - target = self.cal.nlp("Buy a balloon in a month", start) - self.assertEqual(target[0][4], "in a month") - - # Should no longer pull "on" off the end of balloon - target = self.cal.nlp("Buy a balloon Monday", start) - self.assertEqual(target[0][4], "Monday") - - def testFalsePositives(self): - # negative testing - no matches should return None - start = datetime.datetime(2013, 8, 1, 21, 25, 0).timetuple() - self.assertExpectedResult(self.cal.nlp( - "Next, I'm so excited!! So many things that are going to " - "happen every week!!", start), None) - self.assertExpectedResult(self.cal.nlp("$300", start), None) - self.assertExpectedResult(self.cal.nlp("300ml", start), None) - self.assertExpectedResult(self.cal.nlp("nice ass", start), None) - - def testTimes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - targets = { - datetime.datetime(self.yr, self.mth, self.dy, 23, 0, 0): ( - '11:00:00 PM', - '11:00 PM', - '11 PM', - '11PM', - '2300', - '23:00', - '11p', - '11pm', - '11:00:00 P.M.', - '11:00 P.M.', - '11 P.M.', - '11P.M.', - '11p.m.', - '11 p.m.', - ), - datetime.datetime(self.yr, self.mth, self.dy, 11, 0, 0): ( - '11:00:00 AM', - '11:00 AM', - '11 AM', - '11AM', - '1100', - '11:00', - '11a', - '11am', - '11:00:00 A.M.', - '11:00 A.M.', - '11 A.M.', - '11A.M.', - '11a.m.', - '11 a.m.', - ), - datetime.datetime(self.yr, self.mth, self.dy, 7, 30, 0): ( - '730', - '0730', - '0730am', - ), - datetime.datetime(self.yr, self.mth, self.dy, 17, 30, 0): ( - '1730', - '173000', - ) - } - - for dt, phrases in targets.items(): - # Time (2) phrase starting at index 0 - target = (dt, 2, 0) - for phrase in phrases: - self.assertExpectedResult( - self.cal.nlp(phrase, start), - (target + (len(phrase), phrase),) - ) - - # Wrap in quotes - target = (dt, 2, 1) - for phrase in phrases: - self.assertExpectedResult( - self.cal.nlp('"%s"' % phrase, start), - (target + (len(phrase) + 1, phrase),) - ) - - def testFalsePositiveTimes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - phrases = ( - '$300', - '300ml', - '3:2', - ) - - for phrase in phrases: - self.assertExpectedResult( - self.cal.nlp(phrase, start), None) - - def testDates(self): - # Set month to January to avoid issues with August being interpreted - # as next year when tests are run after Aug 25 - start = datetime.datetime( - self.yr, 1, self.dy, self.hr, self.mn, self.sec).timetuple() - targets = { - datetime.datetime(2006, 8, 25, self.hr, self.mn, self.sec): ( - '08/25/2006', - '08.25.2006', - '2006/08/25', - '2006/8/25', - '2006-08-25', - '8/25/06', - 'August 25, 2006', - 'Aug 25, 2006', - 'Aug. 25, 2006', - 'August 25 2006', - 'Aug 25 2006', - 'Aug. 25 2006', - '25 August 2006', - '25 Aug 2006', - ), - datetime.datetime(self.yr, 8, 25, self.hr, self.mn, self.sec): ( - '8/25', - '8.25', - '08/25', - 'August 25', - 'Aug 25', - 'Aug. 25', - ), - datetime.datetime(2006, 8, 1, self.hr, self.mn, self.sec): ( - 'Aug. 2006', - ) - } - - for dt, phrases in targets.items(): - # Date (1) phrase starting at index 0 - target = (dt, 1, 0) - for phrase in phrases: - self.assertExpectedResult( - self.cal.nlp(phrase, start), - (target + (len(phrase), phrase),) - ) - - # Wrap in quotes - target = (dt, 1, 1) - for phrase in phrases: - self.assertExpectedResult( - self.cal.nlp('"%s"' % phrase, start), - (target + (len(phrase) + 1, phrase),) - ) - - def testFalsePositiveDates(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - phrases = ( - '$1.23', - '$12.34' - ) - - for phrase in phrases: - self.assertExpectedResult( - self.cal.nlp(phrase, start), None) diff --git a/tests/TestPhrases.py b/tests/TestPhrases.py deleted file mode 100644 index d6cac4c..0000000 --- a/tests/TestPhrases.py +++ /dev/null @@ -1,160 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of strings that are phrases -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.day_start_hour = 9 - self.cal = pdt.Calendar( - day_start_hour=self.day_start_hour) - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testPhrases(self): - # - # NOTE - this test will fail under certain conditions - # It is building an absolute date for comparison and then - # testing the parsing of relative phrases and as such will fail - # if run near the midnight transition. - # Thanks to Chris Petrilli for asking about it and prompting me - # to create this note! - # - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 16, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('flight from SFO at 4pm', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('eod', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('meeting eod', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('eod meeting', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 0, 0) + datetime.timedelta(days=1) - target = target.timetuple() - - self.assertExpectedResult( - self.cal.parse('tomorrow eod', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('eod tomorrow', start), (target, 3)) - - def testPhraseWithDays_DOWStyle_1_False(self): - s = datetime.datetime.now() - - # find out what day we are currently on - # and determine what the next day of week is - t = s + datetime.timedelta(days=1) - start = s.timetuple() - - (yr, mth, dy, _, _, _, wd, yd, isdst) = t.timetuple() - - target = (yr, mth, dy, 17, 0, 0, wd, yd, isdst) - - d = self.wd + 1 - if d > 6: - d = 0 - - day = self.cal.ptc.Weekdays[d] - - self.assertExpectedResult( - self.cal.parse('eod %s' % day, start), (target, 3)) - - # find out what day we are currently on - # and determine what the previous day of week is - t = s + datetime.timedelta(days=6) - - (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = t.timetuple() - - target = (yr, mth, dy, 17, 0, 0, wd, yd, isdst) - - d = self.wd - 1 - if d < 0: - d = 6 - - day = self.cal.ptc.Weekdays[d] - - self.assertExpectedResult( - self.cal.parse('eod %s' % day, start), (target, 3)) - - def testEndOfPhrases(self): - s = datetime.datetime.now() - - # find out what month we are currently on - # set the day to 1 and then go back a day - # to get the end of the current month - (yr, mth, _, hr, mn, sec, _, _, _) = s.timetuple() - - mth += 1 - if mth > 12: - mth = 1 - yr += 1 - - t = (datetime.datetime( - yr, mth, 1, self.day_start_hour, 0, 0) + - datetime.timedelta(days=-1)) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('eom', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('meeting eom', start), (target, 1)) - - s = datetime.datetime.now() - - (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = s.timetuple() - - t = datetime.datetime( - yr, 12, 31, self.day_start_hour, 0, 0) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('eoy', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('meeting eoy', start), (target, 1)) - - def testLastPhrases(self): - for day in (11, 12, 13, 14, 15, 16, 17): - start = datetime.datetime(2012, 11, day, 9, 0, 0) - - (yr, mth, dy, _, _, _, wd, yd, isdst) = start.timetuple() - - n = 4 - wd - if n >= 0: - n -= 7 - - target = start + datetime.timedelta(days=n) - - self.assertExpectedResult( - self.cal.parse('last friday', start.timetuple()), - (target.timetuple(), 1), dateOnly=True) diff --git a/tests/TestRanges.py b/tests/TestRanges.py deleted file mode 100644 index 68feae6..0000000 --- a/tests/TestRanges.py +++ /dev/null @@ -1,110 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of simple date and times -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTupleRangesAndFlags( - result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testTimes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - - targetStart = datetime.datetime( - self.yr, self.mth, self.dy, 14, 0, 0).timetuple() - targetEnd = datetime.datetime( - self.yr, self.mth, self.dy, 17, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.evalRanges( - "2 pm - 5:30 pm", start), (targetStart, targetEnd, 2)) - self.assertExpectedResult(self.cal.evalRanges( - "2pm - 5:30pm", start), (targetStart, targetEnd, 2)) - self.assertExpectedResult(self.cal.evalRanges( - "2:00:00 pm - 5:30:00 pm", start), (targetStart, targetEnd, 2)) - self.assertExpectedResult(self.cal.evalRanges( - "2 - 5:30pm", start), (targetStart, targetEnd, 2)) - self.assertExpectedResult(self.cal.evalRanges( - "14:00 - 17:30", start), (targetStart, targetEnd, 2)) - - targetStart = datetime.datetime( - self.yr, self.mth, self.dy, 10, 0, 0).timetuple() - targetEnd = datetime.datetime( - self.yr, self.mth, self.dy, 13, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.evalRanges( - "10AM - 1:30PM", start), (targetStart, targetEnd, 2)) - self.assertExpectedResult(self.cal.evalRanges( - "10:00:00 am - 1:30:00 pm", start), (targetStart, targetEnd, 2)) - self.assertExpectedResult(self.cal.evalRanges( - "10:00 - 13:30", start), (targetStart, targetEnd, 2)) - - targetStart = datetime.datetime( - self.yr, self.mth, self.dy, 15, 30, 0).timetuple() - targetEnd = datetime.datetime( - self.yr, self.mth, self.dy, 17, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.evalRanges("today 3:30-5PM", start), - (targetStart, targetEnd, 2)) - - def testDates(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - - targetStart = datetime.datetime( - 2006, 8, 29, self.hr, self.mn, self.sec).timetuple() - targetEnd = datetime.datetime( - 2006, 9, 2, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.evalRanges("August 29, 2006 - September 2, 2006", start), - (targetStart, targetEnd, 1)) - self.assertExpectedResult( - self.cal.evalRanges("August 29 - September 2, 2006", start), - (targetStart, targetEnd, 1)) - - targetStart = datetime.datetime( - 2006, 8, 29, self.hr, self.mn, self.sec).timetuple() - targetEnd = datetime.datetime( - 2006, 9, 2, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.evalRanges("08/29/06 - 09/02/06", start), - (targetStart, targetEnd, 1)) - - def _testSubRanges(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - - targetStart = datetime.datetime(2006, 8, 1, 9, 0, 0).timetuple() - targetEnd = datetime.datetime(2006, 8, 15, 9, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.evalRanges("August 1-15, 2006", start), - (targetStart, targetEnd, 1)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestRussianLocale.py b/tests/TestRussianLocale.py deleted file mode 100644 index b308044..0000000 --- a/tests/TestRussianLocale.py +++ /dev/null @@ -1,138 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of simple date and times using the Russian locale -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - locale = 'ru_RU' - self.ptc = pdt.Constants(locale, usePyICU=False) - self.cal = pdt.Calendar(self.ptc) - - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - if self.ptc.localeID != locale: - raise unittest.SkipTest( - 'Locale not set to %s - check if PyICU is installed' % locale) - - def testTimes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 23, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('23:00:00', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('23:00', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('2300', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 11, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('11:00:00', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('11:00', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('1100', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 7, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('0730', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('1730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('173000', start), (target, 2)) - - def testDates(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - 2006, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('25.08.2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25.8.06', start), (target, 1)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 25): - target = datetime.datetime( - self.yr + 1, 8, 25, self.hr, self.mn, self.sec).timetuple() - else: - target = datetime.datetime( - self.yr, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult(self.cal.parse('25.8', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('25.08', start), (target, 1)) - - def testDatesLang(self): - if sys.version_info >= (3, 0): - target = datetime.datetime(2006, 8, 25, 23, 5).timetuple() - self.assertExpectedResult( - self.cal.parse('25 августа 2006 23:05'), (target, 3)) - target = datetime.datetime( - 2006, 8, 25, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('25 августа 2006'), (target, 1)) - self.assertEqual(self.cal.parse('23:05')[0][3], 23) - self.assertEqual(self.cal.parse('23:05')[0][4], 5) - - def testConjugate(self): - if sys.version_info >= (3, 0): - target = datetime.datetime(2006, 9, 25, 23, 5).timetuple() - self.assertExpectedResult( - self.cal.parse('25 сентября 2006 23:05'), (target, 3)) - # self.assertExpectedResult( - # self.cal.parse('25 сентябрь 2006 23:05'), (target, 3)) - - # does not work with travis - # datetime.now() return non correct data - # def testdayOffsets(self): - # def get_datetime(tuple_time): - # return datetime.datetime(*tuple_time[:6]).date() - # - # now = datetime.datetime.today().date() - # - # self.assertEqual( - # get_datetime(self.cal.parse("вчера")[0]), - # now - datetime.timedelta(days=1) - # ) - # self.assertEqual( - # get_datetime(self.cal.parse("завтра")[0]), - # now + datetime.timedelta(days=1) - # ) - # - # self.assertEqual( - # get_datetime(self.cal.parse("позавчера")[0]), - # now - datetime.timedelta(days=2) - # ) - # - # self.assertEqual( - # get_datetime(self.cal.parse("послезавтра")[0]), - # now + datetime.timedelta(days=2) - # ) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestSimpleDateTimes.py b/tests/TestSimpleDateTimes.py deleted file mode 100644 index 7eca820..0000000 --- a/tests/TestSimpleDateTimes.py +++ /dev/null @@ -1,576 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of simple date and times -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import string -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testDays(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(days=1) - - start = s.timetuple() - target = t.timetuple() - - d = self.wd + 1 - - if d > 6: - d = 0 - - day = self.cal.ptc.Weekdays[d] - - self.assertExpectedResult(self.cal.parse(day, start), (target, 1)) - - t = s + datetime.timedelta(days=6) - - target = t.timetuple() - - d = self.wd - 1 - - if d < 0: - d = 6 - - day = self.cal.ptc.Weekdays[d] - - self.assertExpectedResult(self.cal.parse(day, start), (target, 1)) - - def testTimes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 23, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('11:00:00 PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00 PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11 PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('2300', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('23:00', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11p', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00:00 P.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00 P.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11 P.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11P.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11p.m.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11 p.m.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('"11 p.m."', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 11, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('11:00:00 AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00 AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11 AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1100', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11a', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11am', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00:00 A.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11:00 A.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11 A.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11A.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11a.m.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('11 a.m.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('(11 a.m.)', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 7, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('0730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('0730am', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 17, 30, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('1730', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('173000', start), (target, 2)) - - # Should not parse as a time due to prefix - self.assertExpectedResult(self.cal.parse('$300', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('300ml', start), (start, 0)) - - # Should not parse as a time due to false meridian - self.assertExpectedResult(self.cal.parse('3 axmx', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('3 pxmx', start), (start, 0)) - - def testDates(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - 2006, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('08/25/2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('08.25.2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('2006/08/25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('2006/8/25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('2006-08-25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('8/25/06', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('August 25, 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 25, 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 25, 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('August 25 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 25 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 25 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25 August 2006', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('25 Aug 2006', start), (target, 1)) - - if self.mth > 8 or (self.mth == 8 and self.dy > 25): - target = datetime.datetime( - self.yr + 1, 8, 25, self.hr, self.mn, self.sec).timetuple() - else: - target = datetime.datetime( - self.yr, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('8/25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('8.25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('08/25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('August 25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 25', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('"8.25"', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('(8.25)', start), (target, 1)) - - # Should not parse as dates - self.assertExpectedResult(self.cal.parse('$1.23', start), (start, 0)) - self.assertExpectedResult(self.cal.parse('$12.34', start), (start, 0)) - - # added test to ensure 4-digit year is recognized in the absence of day - target = datetime.datetime( - 2013, 8, 1, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('Aug. 2013', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 2013', start), (target, 1)) - - def testLeapDays(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - 2000, 2, 29, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('02/29/2000', start), (target, 1)) - - target = datetime.datetime( - 2004, 2, 29, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('02/29/2004', start), (target, 1)) - - target = datetime.datetime( - 2008, 2, 29, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('02/29/2008', start), (target, 1)) - - target = datetime.datetime( - 2012, 2, 29, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('02/29/2012', start), (target, 1)) - - dNormal = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) - dLeap = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) - - for i in range(1, 12): - self.assertTrue(self.cal.ptc.daysInMonth(i, 1999), dNormal[i - 1]) - self.assertTrue(self.cal.ptc.daysInMonth(i, 2000), dLeap[i - 1]) - self.assertTrue(self.cal.ptc.daysInMonth(i, 2001), dNormal[i - 1]) - self.assertTrue(self.cal.ptc.daysInMonth(i, 2002), dNormal[i - 1]) - self.assertTrue(self.cal.ptc.daysInMonth(i, 2003), dNormal[i - 1]) - self.assertTrue(self.cal.ptc.daysInMonth(i, 2004), dLeap[i - 1]) - self.assertTrue(self.cal.ptc.daysInMonth(i, 2005), dNormal[i - 1]) - - def testDaySuffixes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - 2008, 8, 22, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('August 22nd, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 22nd, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 22nd, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('August 22nd 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 22nd 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 22nd 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('22nd August 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('22nd Aug 2008', start), (target, 1)) - - target = datetime.datetime( - 1949, 12, 31, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('December 31st, 1949', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Dec 31st, 1949', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('December 31st 1949', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Dec 31st 1949', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('31st December 1949', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('31st Dec 1949', start), (target, 1)) - - target = datetime.datetime( - 2008, 8, 23, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('August 23rd, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 23rd, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 23rd, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('August 23rd 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 23rd 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 23rd 2008', start), (target, 1)) - - target = datetime.datetime( - 2008, 8, 25, self.hr, self.mn, self.sec).timetuple() - - self.assertExpectedResult( - self.cal.parse('August 25th, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 25th, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 25th, 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('August 25th 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug 25th 2008', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('Aug. 25th 2008', start), (target, 1)) - - def testSpecialTimes(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 6, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('morning', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 8, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('breakfast', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 12, 0, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('lunch', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 13, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('afternoon', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 18, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('evening', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 19, 0, 0).timetuple() - - self.assertExpectedResult(self.cal.parse('dinner', start), (target, 2)) - - target = datetime.datetime( - self.yr, self.mth, self.dy, 21, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('night', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('tonight', start), (target, 2)) - - def testMidnight(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 0, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('midnight', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00:00 AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00 AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12 AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12AM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12am', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12a', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('0000', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('00:00', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00:00 A.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00 A.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12 A.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12A.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12a.m.', start), (target, 2)) - - def testNoon(self): - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime( - self.yr, self.mth, self.dy, 12, 0, 0).timetuple() - - self.assertExpectedResult( - self.cal.parse('noon', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00:00 PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00 PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12 PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12PM', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12p', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1200', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00:00 P.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12:00 P.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12 P.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12P.M.', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('12p.m.', start), (target, 2)) - - def testDaysOfWeek(self): - start = datetime.datetime( - 2014, 10, 25, self.hr, self.mn, self.sec).timetuple() - - target = datetime.datetime( - 2014, 10, 26, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('sunday', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('sun', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 27, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('Monday', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('mon', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 28, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('tuesday', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('tues', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 29, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('wednesday', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('wed', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 30, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('thursday', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('thu', start), (target, 1)) - - target = datetime.datetime( - 2014, 10, 31, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('friday', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('fri', start), (target, 1)) - - target = datetime.datetime( - 2014, 11, 1, self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult( - self.cal.parse('saturday', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('sat', start), (target, 1)) - - def testWordBoundaries(self): - # Ensure that keywords appearing at the start of a word are not parsed - # as if they were standalone keywords. For example, "10 dogs" should - # not be interpreted the same as "10 d" - start = datetime.datetime( - self.yr, self.mth, self.dy, self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime.now().timetuple() - - keywords = [] - loc = self.cal.ptc.locale - - def flattenWeekdays(wds): - return sum([wd.split('|') for wd in wds], []) - - # Test all known keywords for the locale - keywords.extend(loc.meridian) - keywords.extend(flattenWeekdays(loc.Weekdays)) - keywords.extend(flattenWeekdays(loc.shortWeekdays)) - keywords.extend(loc.Months) - keywords.extend(loc.shortMonths) - keywords.extend(loc.numbers.keys()) - keywords.extend(loc.Modifiers.keys()) - keywords.extend(loc.dayOffsets.keys()) - keywords.extend(loc.re_sources.keys()) - keywords.extend(loc.small.keys()) - keywords.extend(loc.magnitude.keys()) - - for units in loc.units.values(): - keywords.extend(units) - - # Finally, test all lowercase letters to be particularly thorough - it - # would be very difficult to track down bugs due to single letters. - keywords.extend(list(string.ascii_lowercase)) - - for keyword in keywords: - phrase = '1 %sfoo' % keyword - self.assertExpectedResult( - self.cal.parse(phrase, start), (target, 0), - 'Result does not match target value: %s' % repr(phrase)) - - def testYearParseStyle(self): - config = pdt.Constants() - config.YearParseStyle = 0 - calendar = pdt.Calendar(config) - start = datetime.datetime(self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec).timetuple() - target = datetime.datetime(self.yr, 7, 28, - self.hr, self.mn, self.sec).timetuple() - self.assertExpectedResult(calendar.parse('7/28', start), (target, 1)) - - # def testMonths(self): - - # start = datetime.datetime( - # self.yr, self.mth, self.dy, - # self.hr, self.mn, self.sec).timetuple() - - # target = datetime.datetime( - # self.yr, self.mth, self.dy, 12, 0, 0).timetuple() - - # self.assertExpectedResult(self.cal.parse('jun', start), (target, 2)) - # self.assertExpectedResult( - # self.cal.parse('12:00:00 PM', start), (target, 2)) - # self.assertExpectedResult( - # self.cal.parse('12:00 PM', start), (target, 2)) - # self.assertExpectedResult(self.cal.parse('12 PM', start), - # (target, 2)) - # self.assertExpectedResult(self.cal.parse('12PM', start), (target, 2)) - # self.assertExpectedResult(self.cal.parse('12pm', start), (target, 2)) - # self.assertExpectedResult(self.cal.parse('12p', start), (target, 2)) - # self.assertExpectedResult(self.cal.parse('1200', start), (target, 2)) - # self.assertExpectedResult(self.cal.parse('12:00', start), - # (target, 2)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestSimpleOffsets.py b/tests/TestSimpleOffsets.py deleted file mode 100644 index 5ad9f8d..0000000 --- a/tests/TestSimpleOffsets.py +++ /dev/null @@ -1,287 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of 'simple' offsets -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import calendar -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -def _truncateResult(result, trunc_seconds=True, trunc_hours=False): - try: - dt, flag = result - except ValueError: - # wtf?! - return result - if trunc_seconds: - dt = dt[:5] + (0,) * 4 - if trunc_hours: - dt = dt[:3] + (0,) * 6 - return dt, flag - - -_tr = _truncateResult - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.day_start_hour = 9 - self.cal = pdt.Calendar( - day_start_hour=self.day_start_hour) - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testNow(self): - s = datetime.datetime.now() - - start = s.timetuple() - target = s.timetuple() - - self.assertExpectedResult(self.cal.parse('now', start), (target, 2)) - - def testRightNow(self): - s = datetime.datetime.now() - - start = s.timetuple() - target = s.timetuple() - - self.assertExpectedResult(self.cal.parse('right now', start), (target, 2)) - - def testOffsetFromDayOfWeek(self): - self.cal.ptc.StartTimeFromSourceTime = True - - s = datetime.datetime(2016, 2, 16) # a Tuesday - t = datetime.datetime(2016, 2, 18) # Thursday of the same week - tPlusOffset = t + datetime.timedelta(hours=1) - - start = s.timetuple() - target = t.timetuple() - targetPlusOffset = tPlusOffset.timetuple() - - self.assertExpectedResult( - self.cal.parse('Thursday', start), (target, 1)) - - self.assertExpectedResult( - self.cal.parse('one hour from Thursday', start), (targetPlusOffset, 3)) - - def testOffsetBeforeDayOfWeek(self): - self.cal.ptc.StartTimeFromSourceTime = True - - s = datetime.datetime(2016, 2, 16) # a Tuesday - t = datetime.datetime(2016, 2, 18) # Thursday of the same week - tPlusOffset = t + datetime.timedelta(hours=-1) - - start = s.timetuple() - target = t.timetuple() - targetPlusOffset = tPlusOffset.timetuple() - - self.assertExpectedResult( - self.cal.parse('Thursday', start), (target, 1)) - - self.assertExpectedResult( - self.cal.parse('one hour before Thursday', start), (targetPlusOffset, 3)) - - def testMinutesFromNow(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(minutes=5) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('5 minutes from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 min from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5m from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in 5 minutes', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in 5 min', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 minutes', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('5 min', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('5m', start), (target, 2)) - - self.assertExpectedResult( - self.cal.parse('five minutes from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five min from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in five minutes', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in five min', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five minutes', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five min', start), (target, 2)) - - def testMinutesBeforeNow(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(minutes=-5) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('5 minutes before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 min before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5m before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 minutes ago', start), (target, 2)) - - self.assertExpectedResult( - self.cal.parse('five minutes before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five min before now', start), (target, 2)) - - def testWeekFromNow(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(weeks=1) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('in 1 week', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 week from now', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('in one week', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('one week from now', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('in a week', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('a week from now', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('in 7 days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('7 days from now', start), (target, 3)) - self.assertExpectedResult( - self.cal.parse('in seven days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('seven days from now', start), (target, 3)) - self.assertEqual(_tr(self.cal.parse('next week', start), - trunc_hours=True), - _tr((target, 1), trunc_hours=True)) - - def testNextWeekDay(self): - start = datetime.datetime.now() - target = start + datetime.timedelta(days=4 + 7 - start.weekday()) - start = start.timetuple() - target = target.timetuple() - - self.assertExpectedResult(self.cal.parse('next friday', start), - (target, 1), dateOnly=True) - self.assertExpectedResult(self.cal.parse('next friday?', start), - (target, 1), dateOnly=True) - self.cal.ptc.StartTimeFromSourceTime = True - self.assertExpectedResult(self.cal.parse('next friday', start), - (target, 1)) - - def testNextWeekDayWithTime(self): - start = datetime.datetime.now() - target = start + datetime.timedelta(days=4 + 7 - start.weekday()) - target = target.replace(hour=13, minute=0, second=0) - target = target.timetuple() - - self.assertExpectedResult(self.cal.parse('next friday at 1pm', start), - (target, 3)) - self.assertExpectedResult(self.cal.parse('1pm next friday', start), - (target, 3)) - - target = start + datetime.timedelta(days=4 - start.weekday()) - target = target.replace(hour=13, minute=0, second=0) - target = target.timetuple() - self.assertExpectedResult(self.cal.parse('1pm this friday', start), - (target, 3)) - - def testWeekBeforeNow(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(weeks=-1) - - start = s.timetuple() - target = t.timetuple() - - self.assertEqual(_tr(self.cal.parse('1 week before now', start)), - _tr((target, 3))) - self.assertEqual(_tr(self.cal.parse('one week before now', start)), - _tr((target, 3))) - self.assertEqual(_tr(self.cal.parse('a week before now', start)), - _tr((target, 3))) - self.assertEqual(_tr(self.cal.parse('7 days before now', start)), - _tr((target, 3))) - self.assertEqual(_tr(self.cal.parse('seven days before now', start)), - _tr((target, 3))) - self.assertEqual(_tr(self.cal.parse('1 week ago', start)), - _tr((target, 1))) - self.assertEqual(_tr(self.cal.parse('a week ago', start)), - _tr((target, 1))) - self.assertEqual(_tr(self.cal.parse('last week', start), - trunc_hours=True), - _tr((target, 1), trunc_hours=True)) - - def testNextMonth(self): - s = (datetime.datetime(self.yr, self.mth, self.dy, - self.hr, self.mn, self.sec) + - datetime.timedelta(days=1)) - t = self.cal.inc(s, year=1) - - start = s.timetuple() - target = t.timetuple() - - phrase = 'next %s %s' % (calendar.month_name[t.month], t.day) - - self.assertEqual(_tr(self.cal.parse(phrase, start)), - _tr((target, 1))) - - def testSpecials(self): - s = datetime.datetime.now() - t = datetime.datetime( - self.yr, self.mth, self.dy, - self.day_start_hour, 0, 0) + datetime.timedelta(days=1) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('tomorrow', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('next day', start), (target, 1)) - - t = datetime.datetime( - self.yr, self.mth, self.dy, - self.day_start_hour, 0, 0) + datetime.timedelta(days=-1) - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('yesterday', start), (target, 1)) - - t = datetime.datetime( - self.yr, self.mth, self.dy, - self.day_start_hour, 0, 0) - target = t.timetuple() - - self.assertExpectedResult(self.cal.parse('today', start), (target, 1)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestSimpleOffsetsHours.py b/tests/TestSimpleOffsetsHours.py deleted file mode 100644 index 786d8f9..0000000 --- a/tests/TestSimpleOffsetsHours.py +++ /dev/null @@ -1,117 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of 'simple' offsets -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testHoursFromNow(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(hours=5) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('5 hours from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hour from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hr from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in 5 hours', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in 5 hour', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('5 hr', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('5h', start), (target, 2)) - - self.assertExpectedResult( - self.cal.parse('five hours from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five hour from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five hr from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in five hours', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in five hour', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five hours', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five hr', start), (target, 2)) - - # Test "an" - t = s + datetime.timedelta(hours=1) - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('an hour from now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('in an hour', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('an hour', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('an hr', start), (target, 2)) - self.assertExpectedResult(self.cal.parse('an h', start), (target, 2)) - - # No match, should require a word boundary - self.assertExpectedResult(self.cal.parse('anhour', start), (start, 0)) - self.assertExpectedResult( - self.cal.parse('an hamburger', start), (start, 0)) - - def testHoursBeforeNow(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(hours=-5) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('5 hours before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hr before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5h before now', start), (target, 2)) - - self.assertExpectedResult( - self.cal.parse('five hours before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five hr before now', start), (target, 2)) - - # Test "an" - t = s + datetime.timedelta(hours=-1) - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('an hour before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('an hr before now', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('an h before now', start), (target, 2)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestSimpleOffsetsNoon.py b/tests/TestSimpleOffsetsNoon.py deleted file mode 100644 index 6ec4cd8..0000000 --- a/tests/TestSimpleOffsetsNoon.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of 'simple' offsets -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testOffsetAfterNoon(self): - s = datetime.datetime(self.yr, self.mth, self.dy, 10, 0, 0) - t = datetime.datetime( - self.yr, self.mth, self.dy, 12, 0, 0) + datetime.timedelta(hours=5) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('5 hours after 12pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five hours after 12pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours after 12 pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours after 12:00pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours after 12:00 pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours after noon', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours from noon', start), (target, 2)) - - def testOffsetBeforeNoon(self): - s = datetime.datetime.now() - t = (datetime.datetime(self.yr, self.mth, self.dy, 12, 0, 0) + - datetime.timedelta(hours=-5)) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('5 hours before noon', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours before 12pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('five hours before 12pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours before 12 pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours before 12:00pm', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('5 hours before 12:00 pm', start), (target, 2)) - - def testOffsetBeforeModifiedNoon(self): - # A contrived test of two modifiers applied to noon - offset by - # -5 from the following day (-5 + 24) - s = datetime.datetime.now() - t = (datetime.datetime(self.yr, self.mth, self.dy, 12, 0, 0) + - datetime.timedelta(hours=-5 + 24)) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('5 hours before next noon', start), (target, 2)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/TestStartTimeFromSourceTime.py b/tests/TestStartTimeFromSourceTime.py deleted file mode 100644 index 5f77f86..0000000 --- a/tests/TestStartTimeFromSourceTime.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of strings that are phrases with the -ptc.StartTimeFromSourceTime flag set to True -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - self.cal.ptc.StartTimeFromSourceTime = True - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testEndOfPhrases(self): - s = datetime.datetime.now() - - # find out what month we are currently on - # set the day to 1 and then go back a day - # to get the end of the current month - (yr, mth, dy, hr, mn, sec, _, _, _) = s.timetuple() - - s = datetime.datetime(yr, mth, dy, 13, 14, 15) - - mth += 1 - if mth > 12: - mth = 1 - yr += 1 - t = datetime.datetime( - yr, mth, 1, 13, 14, 15) + datetime.timedelta(days=-1) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult(self.cal.parse('eom', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('meeting eom', start), (target, 1)) - - s = datetime.datetime.now() - - (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = s.timetuple() - - s = datetime.datetime(yr, mth, 1, 13, 14, 15) - t = datetime.datetime(yr, 12, 31, 13, 14, 15) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult(self.cal.parse('eoy', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('meeting eoy', start), (target, 1)) diff --git a/tests/TestUnits.py b/tests/TestUnits.py deleted file mode 100644 index 34bfa36..0000000 --- a/tests/TestUnits.py +++ /dev/null @@ -1,192 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test parsing of units -""" -from __future__ import unicode_literals - -import sys -import time -import datetime -import parsedatetime as pdt -from . import utils - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -class test(unittest.TestCase): - - @utils.assertEqualWithComparator - def assertExpectedResult(self, result, check, **kwargs): - return utils.compareResultByTimeTuplesAndFlags(result, check, **kwargs) - - def setUp(self): - self.cal = pdt.Calendar() - (self.yr, self.mth, self.dy, self.hr, - self.mn, self.sec, self.wd, self.yd, self.isdst) = time.localtime() - - def testMinutes(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(minutes=1) - h = s - datetime.timedelta(minutes=1) - - start = s.timetuple() - target = t.timetuple() - history = h.timetuple() - - self.assertExpectedResult( - self.cal.parse('1 minutes', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1 minute', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1 min', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1min', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1 m', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1m', start), (target, 2)) - - self.assertExpectedResult( - self.cal.parse('1 minutes ago', start), (history, 2)) - self.assertExpectedResult( - self.cal.parse('1 minute ago', start), (history, 2)) - - def testHours(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(hours=1) - h = s - datetime.timedelta(hours=1) - - start = s.timetuple() - target = t.timetuple() - history = h.timetuple() - - self.assertExpectedResult( - self.cal.parse('1 hour', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1 hours', start), (target, 2)) - self.assertExpectedResult( - self.cal.parse('1 hr', start), (target, 2)) - - self.assertExpectedResult( - self.cal.parse('1 hour ago', start), (history, 2)) - self.assertExpectedResult( - self.cal.parse('1 hours ago', start), (history, 2)) - - def testDays(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(days=1) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult(self.cal.parse('1 day', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('1 days', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('1days', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('1 dy', start), (target, 1)) - self.assertExpectedResult(self.cal.parse('1 d', start), (target, 1)) - - def testNegativeDays(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(days=-1) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('-1 day', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('-1 days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('-1days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('-1 dy', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('-1 d', start), (target, 1)) - - self.assertExpectedResult( - self.cal.parse('- 1 day', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('- 1 days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('- 1days', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('- 1 dy', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('- 1 d', start), (target, 1)) - - self.assertExpectedResult( - self.cal.parse('1 day ago', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 days ago', start), (target, 1)) - - def testWeeks(self): - s = datetime.datetime.now() - t = s + datetime.timedelta(weeks=1) - h = s - datetime.timedelta(weeks=1) - - start = s.timetuple() - target = t.timetuple() - history = h.timetuple() - - self.assertExpectedResult( - self.cal.parse('1 week', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1week', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 weeks', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 wk', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 w', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1w', start), (target, 1)) - - self.assertExpectedResult( - self.cal.parse('1 week ago', start), (history, 1)) - self.assertExpectedResult( - self.cal.parse('1 weeks ago', start), (history, 1)) - - def testMonths(self): - s = datetime.datetime.now() - t = self.cal.inc(s, month=1) - h = self.cal.inc(s, month=-1) - - start = s.timetuple() - target = t.timetuple() - history = h.timetuple() - - self.assertExpectedResult( - self.cal.parse('1 month', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 months', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1month', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 month ago', start), (history, 1)) - self.assertExpectedResult( - self.cal.parse('1 months ago', start), (history, 1)) - - def testYears(self): - s = datetime.datetime.now() - t = self.cal.inc(s, year=1) - - start = s.timetuple() - target = t.timetuple() - - self.assertExpectedResult( - self.cal.parse('1 year', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 years', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 yr', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1 y', start), (target, 1)) - self.assertExpectedResult( - self.cal.parse('1y', start), (target, 1)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/__init__.py b/tests/__init__.py index 6692b99..4230ad8 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,9 +3,9 @@ The tests can be run as a C{suite} by running:: - nosetests + py.test -Requires Python 3.0 or later +Requires Python 2.7 or later """ import logging diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..dad9ff4 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +""" +Preloads fixtures and adds any test hooks or plugins. +""" + +import pytest + +from tests.lib.assertions import * +from tests.lib.fixtures import * + + +@pytest.hookimpl(hookwrapper=True) +def pytest_runtest_makereport(item, call): + outcome = yield + rep = outcome.get_result() + + if rep.skipped and call.excinfo.errisinstance(pytest.skip.Exception): + rep.outcome = 'failed' + r = call.excinfo._getreprcrash() + message = 'Invalid test configuration' + if 'empty parameter set' in r.message: + message = 'No data found for test group' + rep.longrepr = '%s - %s' % (message, r.message) diff --git a/tests/data/de_DE/deltas.yml b/tests/data/de_DE/deltas.yml new file mode 100644 index 0000000..ceafbc9 --- /dev/null +++ b/tests/data/de_DE/deltas.yml @@ -0,0 +1,26 @@ +constants: + - &arbitrary_date 2016-01-01 00:00:00 + +# +# Single unit deltas +# + +past_integer_values: + sourceTime: *arbitrary_date + cases: + - context: !pdtContext minute + target: !datedelta + minutes: -5 + phrases: + - "-5 minuten" + - context: !pdtContext hour + target: !datedelta + hours: -34 + phrases: + - "-34 stunden" + - context: !pdtContext day + target: !datedelta + days: -2 + phrases: + - "-2 tage" + diff --git a/tests/data/en_US/complex_datetimes.yml b/tests/data/en_US/complex_datetimes.yml new file mode 100644 index 0000000..0f7f725 --- /dev/null +++ b/tests/data/en_US/complex_datetimes.yml @@ -0,0 +1,171 @@ +year_month_day_and_time: + cases: + - target: 2014-08-05 04:15:00 + context: !pdtContext year | month | day | hour | minute + phrases: + - "Aug 05, 2014 4:15 AM" + - target: 2003-08-05 03:15:00 + context: !pdtContext year | month | day | hour | minute + phrases: + - "Aug 05, 2003 3:15 AM" + - target: 2003-08-05 03:15:00 + context: !pdtContext year | month | day | hour | minute + phrases: + - "Aug 05, 2003 3:15 AM" + - "Aug 05, 2003 03:15 AM" + +future_month_day_and_time: + # Months below are after May + sourceTime: !replace xxxx-05-xx xx:xx:xx + cases: + - target: !replace xxxx-06-30 12:00:00 + context: !pdtContext month | day | hour + phrases: + - "June 30th 12PM" + - target: !replace xxxx-06-30 12:00:00 + context: !pdtContext month | day | hour | minute + phrases: + - "June 30th 12:00" + - target: !replace xxxx-11-30 23:00:00 + context: !pdtContext month | day | hour + phrases: + - "November 30th 23PM" + - target: !replace xxxx-11-30 23:02:00 + context: !pdtContext month | day | hour | minute + phrases: + - "November 30th 23:02" + +past_month_day_and_time: + # Months below are before December + sourceTime: !replace xxxx-12-xx xx:xx:xx + cases: + - target: !datedelta + sourceTime: !replace xxxx-06-30 12:00:00 + years: 1 + context: !pdtContext month | day | hour + phrases: + - "June 30th 12PM" + - target: !datedelta + sourceTime: !replace xxxx-06-30 12:00:00 + years: 1 + context: !pdtContext month | day | hour | minute + phrases: + - "June 30th 12:00" + - target: !datedelta + sourceTime: !replace xxxx-11-30 23:00:00 + years: 1 + context: !pdtContext month | day | hour + phrases: + - "November 30th 23PM" + - target: !datedelta + sourceTime: !replace xxxx-11-30 23:02:00 + years: 1 + context: !pdtContext month | day | hour | minute + phrases: + - "November 30th 23:02" + +date_variations: + cases: + - target: 2006-08-25 17:00:00 + context: !pdtContext year | month | day | hour + phrases: + - "08/25/2006 5pm" + - "5pm on 08.25.2006" + - "5pm August 25, 2006" + - "5pm August 25th, 2006" + - "5pm 25 August, 2006" + - "5pm 25th August, 2006" + - "Aug 25, 2006 5pm" + - "Aug 25th, 2006 5pm" + - "25 Aug, 2006 5pm" + - "25th Aug 2006, 5pm" + +future_date_variations: + sourceTime: !replace xxxx-01-xx xx:xx:xx + cases: + - target: !replace xxxx-08-05 17:00:00 + context: !pdtContext month | day | hour + phrases: + - "8/5 at 5pm" + - "5pm 8.5" + - "08/05 5pm" + - "August 5 5pm" + - "5pm Aug 05" + - "Aug 05 5pm" + - "Aug 05th 5pm" + - "5 August 5pm" + - "5th August 5pm" + - "5pm 05 Aug" + - "05 Aug 5pm" + - "05th Aug 5pm" + - "August 5th 5pm" + - target: !replace xxxx-08-05 12:00:00 + context: !pdtContext month | day | hour + phrases: + - "August 5th 12pm" + - "August 5th 12 pm" + - target: !replace xxxx-08-05 12:00:00 + context: !pdtContext month | day | hour | minute + phrases: + - "August 5th 12:00" + - "August 5th 12:00pm" + - "August 5th 12:00 pm" + - target: !replace xxxx-08-22 03:26:00 + context: !pdtContext month | day | hour | minute + phrases: + - "August 22nd 3:26" + - "August 22nd 3:26am" + - "August 22nd 3:26 am" + +past_date_variations: + sourceTime: !replace xxxx-10-xx xx:xx:xx + cases: + - target: !datedelta + sourceTime: !replace xxxx-08-05 17:00:00 + years: 1 + context: !pdtContext month | day | hour + phrases: + - "8/5 at 5pm" + - "5pm 8.5" + - "08/05 5pm" + - "August 5 5pm" + - "5pm Aug 05" + - "Aug 05 5pm" + - "Aug 05th 5pm" + - "5 August 5pm" + - "5th August 5pm" + - "5pm 05 Aug" + - "05 Aug 5pm" + - "05th Aug 5pm" + - "August 5th 5pm" + - target: !datedelta + sourceTime: !replace xxxx-08-05 12:00:00 + years: 1 + context: !pdtContext month | day | hour + phrases: + - "August 5th 12pm" + - "August 5th 12 pm" + - target: !datedelta + sourceTime: !replace xxxx-08-05 12:00:00 + years: 1 + context: !pdtContext month | day | hour | minute + phrases: + - "August 5th 12:00" + - "August 5th 12:00pm" + - "August 5th 12:00 pm" + - target: !datedelta + sourceTime: !replace xxxx-08-22 03:26:00 + years: 1 + context: !pdtContext month | day | hour | minute + phrases: + - "August 22nd 3:26" + - "August 22nd 3:26am" + - "August 22nd 3:26 am" + +dates_with_weekday: + cases: + - target: 2016-08-23 17:00:00 + context: !pdtContext year | month | day | hour + phrases: + # FIXME: 23nd? Not a future-proof test. + - "tuesday august 23nd 2016 at 5pm" diff --git a/tests/data/en_US/day_start_hour.yml b/tests/data/en_US/day_start_hour.yml new file mode 100644 index 0000000..01a500a --- /dev/null +++ b/tests/data/en_US/day_start_hour.yml @@ -0,0 +1,87 @@ +constants: + - &next_day_phrases + - "tomorrow" + - "next day" + - &previous_day_phrases + - "yesterday" + - &same_day_phrases + - "today" + - &default_day_start_hour !replace xxxx-xx-xx 09:00:00 + +default_day_start_hour: + cases: + - target: !datedelta + sourceTime: *default_day_start_hour + days: 1 + context: !pdtContext day + phrases: *next_day_phrases + - target: !datedelta + sourceTime: *default_day_start_hour + days: -1 + context: !pdtContext day + phrases: *previous_day_phrases + - target: !datedelta + sourceTime: *default_day_start_hour + days: 0 + context: !pdtContext day + phrases: *same_day_phrases + +midnight_day_start_hour: + options: + day_start_hour: 0 + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 00:00:00 + days: 1 + context: !pdtContext day + phrases: *next_day_phrases + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 00:00:00 + days: -1 + context: !pdtContext day + phrases: *previous_day_phrases + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 00:00:00 + days: 0 + context: !pdtContext day + phrases: *same_day_phrases + +morning_day_start_hour: + options: + day_start_hour: 3 + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 03:00:00 + days: 1 + context: !pdtContext day + phrases: *next_day_phrases + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 03:00:00 + days: -1 + context: !pdtContext day + phrases: *previous_day_phrases + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 03:00:00 + days: 0 + context: !pdtContext day + phrases: *same_day_phrases + +evening_day_start_hour: + options: + day_start_hour: 19 + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 19:00:00 + days: 1 + context: !pdtContext day + phrases: *next_day_phrases + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 19:00:00 + days: -1 + context: !pdtContext day + phrases: *previous_day_phrases + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 19:00:00 + days: 0 + context: !pdtContext day + phrases: *same_day_phrases \ No newline at end of file diff --git a/tests/data/en_US/deltas.yml b/tests/data/en_US/deltas.yml new file mode 100644 index 0000000..ffb12c6 --- /dev/null +++ b/tests/data/en_US/deltas.yml @@ -0,0 +1,123 @@ +constants: + - &arbitrary_date 2016-01-01 00:00:00 + - &delta_3y2w5d !datedelta + years: 3 + weeks: 2 + days: 5 + - &delta_2m1h4m32s !datedelta + months: 2 + hours: 1 + minutes: 4 + seconds: 32 + +# +# Single unit deltas +# + +past_integer_values: + sourceTime: *arbitrary_date + cases: + - context: !pdtContext minute + target: !datedelta + minutes: -5 + phrases: + - "5 minutes ago" + - context: !pdtContext hour + target: !datedelta + hours: -34 + phrases: + - "34 hours ago" + - context: !pdtContext day + target: !datedelta + days: -2 + phrases: + - "2 days ago" + + +past_float_values: + sourceTime: *arbitrary_date + cases: + - target: !datedelta + minutes: -58.4 + phrases: + - "58.4 minutes ago" + + - target: !datedelta + minutes: -1855336.424 + phrases: + - "1855336.424 minutes ago" + + - target: !datedelta + hours: -8.3 + phrases: + - "8.3 hours ago" + + - target: !datedelta + hours: -22.355 + phrases: + - "22.355 hours ago" + +# +# Multiple unit deltas +# + +simple_multiple_unit_deltas: + sourceTime: *arbitrary_date + cases: + - target: *delta_3y2w5d + context: !pdtContext year | week | day + phrases: + - "3 years, 2 weeks, 5 days" + - "3 years, two weeks and five days" + - "5 day 3 week 2 year" + - "2weeks, 5days, 2year" + cases: + - target: *delta_2m1h4m32s + context: !pdtContext month | hour | minute | second + phrases: + - "2 months, an hour, four minutes, 32 seconds" + - "2 months, 1 hour, 4 minutes, and 32 seconds" + - "32 second 4 minute 2 month 1 hour" + - "2months, 4minute, 32second, 1hour" + +abbreviated_multiple_unit_deltas: + sourceTime: *arbitrary_date + cases: + - target: *delta_3y2w5d + context: !pdtContext year | week | day + phrases: + - "3 yrs, 2 wks, 5 dys" + - "3 y, 2 w and 5 d" + - "5dy 3w 2yr" + - "2wk, 5d, 2y" + cases: + # Note: there is no single-letter abbreviation for "month" in English + # locales + - target: *delta_2m1h4m32s + context: !pdtContext month | hour | minute | second + phrases: + - "2 mth, 1 hr, 4 min, 32 sec" + - "2 mth, 1 h, 4 m, and 32 s" + - "32sec 4m 2mth 1h" + - "2mth, 4min, 32s, 1hr" + +unix_at_style: + sourceTime: 2016-04-05 06:07:08 + cases: + - target: !datedelta + sourceTime: 2016-04-05 16:00:00 + days: 3 + context: !pdtContext hour | day + phrases: + - "4pm + 3 days" + - "4pm +3 days" + +unix_at_style_negative: + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 16:00:00 + days: -3 + context: !pdtContext hour | day + phrases: + - "4pm - 3 days" + - "4pm -3 days" diff --git a/tests/data/en_US/errors.yml b/tests/data/en_US/errors.yml new file mode 100644 index 0000000..01bb194 --- /dev/null +++ b/tests/data/en_US/errors.yml @@ -0,0 +1,52 @@ +out_of_range: + cases: + - target: null + phrases: + # TODO: parse returns the current time rather than sourceTime for these + #- "01/0" + #- "08/35" + #- "18/35" + - "1799" + - "781" + - "2702" + +plain_numbers: + cases: + - target: null + phrases: + - "78" + - "11" + - "1" + - "174565" + - "177505" + +# These phrases contain text like month names or day abbreviations but should +# not be matched since they are part of a longer word. +substring_phrases: + cases: + - target: null + phrases: + - "injunction" # jun + - "julius" # jul + - "lamar" # mar + - "demonize" # mon + - "money" # mon + - "month" # mon + +# These phrases contain date representations within longer words +substring_dates: + cases: + - target: null + phrases: + - "30/030/01/071/07" + +# Python's datetime is not capable of representing dates this far in the future +# due to an overflow +overflow: + cases: + - target: null + phrases: + - "12345 y" + - "654321 w" + - "3700000 d" + \ No newline at end of file diff --git a/tests/data/en_US/names.yml b/tests/data/en_US/names.yml new file mode 100644 index 0000000..5637638 --- /dev/null +++ b/tests/data/en_US/names.yml @@ -0,0 +1,153 @@ +constants: + - &saturdays + - 2011-01-01 01:02:03 + - 2016-02-27 04:05:06 + - &sunday !datedelta + days: 1 + - &monday !datedelta + days: 2 + - &tuesday !datedelta + days: 3 + - &wednesday !datedelta + days: 4 + - &thursday !datedelta + days: 5 + - &friday !datedelta + days: 6 + - &saturday !datedelta + days: 7 + - &january !replace xxxx-01-01 xx:xx:xx + - &february !replace xxxx-02-01 xx:xx:xx + - &march !replace xxxx-03-01 xx:xx:xx + - &april !replace xxxx-04-01 xx:xx:xx + - &may !replace xxxx-05-01 xx:xx:xx + - &june !replace xxxx-06-01 xx:xx:xx + - &july !replace xxxx-07-01 xx:xx:xx + - &august !replace xxxx-08-01 xx:xx:xx + - &september !replace xxxx-09-01 xx:xx:xx + - &october !replace xxxx-10-01 xx:xx:xx + - &november !replace xxxx-11-01 xx:xx:xx + - &december !replace xxxx-12-01 xx:xx:xx + +# +# Single unit deltas +# + +week_day_names: + sourceTime: *saturdays + context: !pdtContext day + cases: + - target: *sunday + phrases: + - "Sunday" + - "Sun" + - target: *monday + phrases: + - "Monday" + - "Mon" + - target: *tuesday + phrases: + - "Tuesday" + - "Tues" + - "Tue" + - target: *wednesday + phrases: + - "Wednesday" + - "Wed" + - target: *thursday + phrases: + - "Thursday" + - "Thu" + - target: *friday + phrases: + - "Friday" + - "Fri" + - target: *saturday + phrases: + - "Saturday" + - "Sat" + +month_names: + # This could be any date from Dec 2 of the previous year to Jan 1 in order + # for all tests to pass, otherwise we have to deal with different years + sourceTime: *january + context: !pdtContext month + cases: + - target: *january + phrases: + - "January" + - "Jan" + - target: *february + phrases: + - "February" + - "Feb" + - target: *march + phrases: + - "March" + - "Mar" + - target: *april + phrases: + - "April" + - "Apr" + - target: *may + phrases: + - "May" + - target: *june + phrases: + - "June" + - "Jun" + - target: *july + phrases: + - "July" + - "Jul" + - target: *august + phrases: + - "August" + - "Aug" + - target: *september + phrases: + - "September" + - "Sep" + - target: *october + phrases: + - "October" + - "Oct" + - target: *november + phrases: + - "November" + - "Nov" + - target: *december + phrases: + - "December" + - "Dec" + +invalid_week_day_names: + context: !pdtContext + cases: + - target: null + phrases: + - "Sund" + - "Suna" + - "Money" + - "Tuesd" + - "atues" + - "Wednesdaying" + - "onthursday" + - "th. ursday" + - "fr iday" + - "Fríday" + - "satur-day" + - "sun_day" # _ is not a word boundary; most characters would match "sun" here + +invalid_month_names: + context: !pdtContext + cases: + - target: null + phrases: + - "Janu" + - "aFebruary" + - "Ma rch" + - "A_pril" + - "Mayjune" + - "Ju" + - "augu.st" diff --git a/tests/data/en_US/nlp.yml b/tests/data/en_US/nlp.yml new file mode 100644 index 0000000..0522636 --- /dev/null +++ b/tests/data/en_US/nlp.yml @@ -0,0 +1,106 @@ +constants: + - &saturdays + - 2016-08-06 08:30:00 + - 2016-02-27 11:11:11 + - &sundays_before_august_5 + - 2013-08-04 21:25:00 + - 2016-07-31 02:04:06 + - &default_day_start_time !replace xxxx-xx-xx 09:00:00 + +long_phrases: + sourceTime: *sundays_before_august_5 + cases: + - target: !nlpTarget + - phrase: "At 8PM on August 5th" + target: !replace xxxx-08-05 20:00:00 + context: !pdtContext month | day | hour + - phrase: "next Friday at 9PM" + target: !datedelta + sourceTime: !replace xxxx-xx-xx 21:00:00 + days: 5 + context: !pdtContext day | hour + - phrase: "in 5 minutes" + target: !datedelta + minutes: 5 + context: !pdtContext minute + - phrase: "next week" + target: !datedelta + sourceTime: *default_day_start_time + days: 7 + context: !pdtContext week + phrases: + - > + I'm so excited!! At 8PM on August 5th i'm going to fly to + Florida. Then next Friday at 9PM i'm going to Dog n Bone! + And in 5 minutes I'm going to eat some food! Talk to you + next week. + +long_phrases_with_quotes: + sourceTime: *sundays_before_august_5 + cases: + - target: !nlpTarget + - phrase: "At '8PM on August 5th" + target: !replace xxxx-08-05 20:00:00 + context: !pdtContext month | day | hour + - phrase: "next Friday at 9PM" + target: !datedelta + sourceTime: !replace xxxx-xx-xx 21:00:00 + days: 5 + context: !pdtContext day | hour + - phrase: "in '5 minutes" + target: !datedelta + minutes: 5 + context: !pdtContext minute + - phrase: "next week" + target: !datedelta + sourceTime: *default_day_start_time + days: 7 + context: !pdtContext week + phrases: + - > + I'm so excited!! At '8PM on August 5th' i'm going to fly to + Florida. Then 'next Friday at 9PM' i'm going to Dog n Bone! + And in '5 minutes' I'm going to eat some food! Talk to you + "next week" + +prefixes: + sourceTime: *saturdays + cases: + - target: !nlpTarget + - phrase: "on Monday" + target: !datedelta + days: 2 + context: !pdtContext day + phrases: + - "Buy a balloon on Monday" + - target: !nlpTarget + - phrase: "at noon" + target: !replace xxxx-xx-xx 12:00:00 + context: !pdtContext halfday + phrases: + - "Buy a balloon at noon" + - target: !nlpTarget + - phrase: "in a month" + target: !datedelta + months: 1 + context: !pdtContext month + phrases: + - "Buy a balloon in a month" + - target: !nlpTarget + - phrase: "Monday" + target: !datedelta + days: 2 + context: !pdtContext day + phrases: + - "Buy a balloon Monday" # Should not pull "on" from the end of "balloon" + +invalid_phrases: + cases: + - target: null + phrases: + - > + Next, I'm so excited!! So many things that are going to + happen every week!! + - "$300" + - "300mL" + - "nice ass" diff --git a/tests/data/en_US/numbers_as_words.yml b/tests/data/en_US/numbers_as_words.yml new file mode 100644 index 0000000..abfc359 --- /dev/null +++ b/tests/data/en_US/numbers_as_words.yml @@ -0,0 +1,53 @@ +# Target values as expected from Calendar._convertUnitAsWords +numbers_as_words: + cases: + - target: 0 + phrases: + - "zero" + - target: 1 + phrases: + - "one" + - target: 11 + phrases: + - "eleven" + - target: 42 + phrases: + - "forty two" + - "forty-two" + - target: 100 + phrases: + - "a hundred" + - "one hundred" + - target: 415 + phrases: + - "four hundred and fifteen" + - "four hundred fifteen" + - target: 12020 + phrases: + - "twelve thousand twenty" + - target: 33100 + phrases: + - "thirty three thousand one hundred" + - "thirty-three thousand and a hundred" + - target: 999 + phrases: + - "nine hundred and ninety nine" + - "nine hundred and ninety-nine" + - "nine hundred ninety nine" + - target: 3000000004000000000 + phrases: + - "three quintillion four billion" + - "three quintillion and four billion" + - target: 43999 + phrases: + - "forty three thousand, nine hundred and ninety nine" + - "forty-three thousand nine hundred and ninety-nine" + - "forty three thousand nine hundred ninety nine" + - target: 133000400314 + phrases: + - "one hundred thirty three billion, four hundred thousand three hundred fourteen" + - "one hundred thirty-three billion four hundred thousand three hundred and fourteen" + - target: 1000000000000000000000000000 + phrases: + - "an octillion" + - "one octillion" \ No newline at end of file diff --git a/tests/data/en_US/phrases.yml b/tests/data/en_US/phrases.yml new file mode 100644 index 0000000..d83d73c --- /dev/null +++ b/tests/data/en_US/phrases.yml @@ -0,0 +1,68 @@ +constants: + - &saturdays + - 2016-02-27 01:02:03 + - 2016-11-19 23:05:06 + +phrases: + cases: + - target: !replace xxxx-xx-xx 16:00:00 + context: !pdtContext hour + phrases: + - "flight from SFO at 4pm" + - target: !replace xxxx-xx-xx 17:00:00 + context: !pdtContext halfday + phrases: + - "meeting eod" + - "eod meeting" + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 17:00:00 + days: 1 + context: !pdtContext day | halfday + phrases: + - "tomorrow eod" + - "eod tomorrow" + +weekday_phrases: + sourceTime: *saturdays + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 17:00:00 + days: 1 + # TODO: There isn't actually an hour component here + context: !pdtContext day | halfday | hour + phrases: + - "eod sunday" + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 17:00:00 + days: 6 + context: !pdtContext day | halfday | hour + phrases: + - "eod friday" + +end_of_phrases: + cases: + - target: !replace xxxx-xx-xx 17:00:00 + context: !pdtContext halfday + phrases: + - "eod" + - target: !datedelta + sourceTime: !replace xxxx-xx-01 09:00:00 + months: 1 + days: -1 + context: !pdtContext day + phrases: + - "eom" + - target: !replace xxxx-12-31 09:00:00 + context: !pdtContext month + phrases: + - "eoy" + +last_phrases: + sourceTime: *saturdays + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 09:00:00 + days: -1 + context: !pdtContext day + phrases: + - "last friday" \ No newline at end of file diff --git a/tests/data/en_US/ranges.yml b/tests/data/en_US/ranges.yml new file mode 100644 index 0000000..927b10d --- /dev/null +++ b/tests/data/en_US/ranges.yml @@ -0,0 +1,49 @@ +times: + cases: + - target: + - !replace xxxx-xx-xx 14:00:00 + - !replace xxxx-xx-xx 17:30:00 + # FIXME: evalRanges does not yet support pdtContext + context: 2 + phrases: + - "2 pm - 5:30 pm" + - "2pm - 5:30pm" + - "2:00:00 pm - 5:30:00 pm" + - "2 - 5:30pm" + - "14:00 - 17:30" + - target: + - !replace xxxx-xx-xx 10:00:00 + - !replace xxxx-xx-xx 13:30:00 + context: 2 + phrases: + - "10AM - 1:30PM" + - "10:00:00 am - 1:30:00 pm" + - "10:00 - 13:30" + - target: + - !replace xxxx-xx-xx 15:30:00 + - !replace xxxx-xx-xx 17:00:00 + context: 2 + phrases: + - "today 3:30-5PM" + +dates: + cases: + - target: + - !replace 2006-08-29 xx:xx:xx + - !replace 2006-09-02 xx:xx:xx + context: 1 + phrases: + - "August 29, 2006 - September 2, 2006" + - "August 29 - September 2, 2006" + - "08/29/06 - 09/02/06" + +# FIXME: Subrange functionality does not work and this test was disabled in +# TestRanges +sub_ranges: + cases: + - target: + - 2006-08-01 09:00:00 + - 2006-08-15 09:00:00 + context: 1 + phrases: + - "August 1-15, 2006" diff --git a/tests/data/en_US/simple_datetimes.yml b/tests/data/en_US/simple_datetimes.yml new file mode 100644 index 0000000..9fd3252 --- /dev/null +++ b/tests/data/en_US/simple_datetimes.yml @@ -0,0 +1,259 @@ +times: + cases: + # 23:00:00 + - target: !replace xxxx-xx-xx 23:00:00 + context: !pdtContext hour | minute | second + phrases: + - "11:00:00 PM" + - "11:00:00 P.M." + - target: !replace xxxx-xx-xx 23:00:00 + context: !pdtContext hour | minute + phrases: + - "11:00 PM" + - "2300" + - "23:00" + - "11:00 P.M." + - target: !replace xxxx-xx-xx 23:00:00 + context: !pdtContext hour + phrases: + - "11 PM" + - "11PM" + - "11p" + - "11pm" + - "11 P.M." + - "11P.M." + - "11p.m." + - "11 p.m." + # 11:00:00 + - target: !replace xxxx-xx-xx 11:00:00 + context: !pdtContext hour | minute | second + phrases: + - "11:00:00 AM" + - "11:00:00 A.M." + - target: !replace xxxx-xx-xx 11:00:00 + context: !pdtContext hour | minute + phrases: + - "11:00 AM" + - "1100" + - "11:00" + - "11:00 A.M." + - target: !replace xxxx-xx-xx 11:00:00 + context: !pdtContext hour + phrases: + - "11 AM" + - "11AM" + - "11a" + - "11am" + - "11 A.M." + - "11A.M." + - "11a.m." + - "11 a.m." + +invalid_times: + cases: + - target: null + phrases: + - "$300" + - "300ml" + - "3:2" + +dates: + sourceTime: !replace xxxx-01-xx xx:xx:xx + cases: + - target: !replace 2006-08-25 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "08/25/2006" + - "08.25.2006" + - "2006/08/25" + - "2006/8/25" + - "2006-08-25" + - "8/25/06" + - "August 25, 2006" + - "Aug 25, 2006" + - "Aug. 25, 2006" + - "August 25 2006" + - "Aug 25 2006" + - "Aug. 25 2006" + - "25 August 2006" + - "25 Aug 2006" + - target: !replace xxxx-08-25 xx:xx:xx + context: !pdtContext month | day + phrases: + - "8/25" + - "8.25" + - "08/25" + - "August 25" + - "Aug 25" + - "Aug. 25" + - target: !replace 2006-08-01 xx:xx:xx + context: !pdtContext year | month + phrases: + - "Aug. 2006" + +invalid_dates: + cases: + - target: null + phrases: + - "$123" + - "$12.34" + +leap_days: + cases: + - target: !replace 2000-02-29 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "02/29/2000" + - target: !replace 2004-02-29 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "02/29/2004" + - target: !replace 2008-02-29 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "02/29/2008" + - target: !replace 2012-02-29 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "02/29/2012" + +day_suffixes: + cases: + - target: !replace 2008-08-22 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "August 22nd, 2008" + - "Aug 22nd, 2008" + - "Aug. 22nd, 2008" + - "August 22nd 2008" + - "Aug 22nd 2008" + - "Aug. 22nd 2008" + - "22nd August 2008" + - "22nd Aug 2008" + - target: !replace 1949-12-31 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "December 31st, 1949" + - "Dec 31st, 1949" + - "December 31st 1949" + - "Dec 31st 1949" + - "31st December 1949" + - "31st Dec 1949" + - target: !replace 2008-08-23 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "August 23rd, 2008" + - "Aug 23rd, 2008" + - "Aug. 23rd, 2008" + - "August 23rd 2008" + - "Aug 23rd 2008" + - "Aug. 23rd 2008" + - target: !replace 2008-08-25 xx:xx:xx + context: !pdtContext year | month | day + phrases: + - "August 25th, 2008" + - "Aug 25th, 2008" + - "Aug. 25th, 2008" + - "August 25th 2008" + - "Aug 25th 2008" + - "Aug. 25th 2008" + +special_times: + cases: + - target: !replace xxxx-xx-xx 06:00:00 + context: !pdtContext halfday + phrases: + - "morning" + - target: !replace xxxx-xx-xx 08:00:00 + context: !pdtContext halfday + phrases: + - "breakfast" + - target: !replace xxxx-xx-xx 12:00:00 + context: !pdtContext halfday + phrases: + - "lunch" + - target: !replace xxxx-xx-xx 13:00:00 + context: !pdtContext halfday + phrases: + - "afternoon" + - target: !replace xxxx-xx-xx 18:00:00 + context: !pdtContext halfday + phrases: + - "evening" + - target: !replace xxxx-xx-xx 19:00:00 + context: !pdtContext halfday + phrases: + - "dinner" + - target: !replace xxxx-xx-xx 21:00:00 + context: !pdtContext halfday + phrases: + - "night" + - "tonight" + +midnight: + cases: + - target: !replace xxxx-xx-xx 00:00:00 + phrases: + - "midnight" + - "12:00:00 AM" + - "12:00 AM" + - "12 AM" + - "12AM" + - "12am" + - "12a" + - "0000" + - "00:00" + - "12:00:00 A.M." + - "12:00 A.M." + - "12 A.M." + - "12A.M." + - "12a.m." + +noon: + cases: + - target: !replace xxxx-xx-xx 12:00:00 + phrases: + - "noon" + - "12:00:00 PM" + - "12:00 PM" + - "12 PM" + - "12PM" + - "12pm" + - "12p" + - "1200" + - "12:00" + - "12:00:00 P.M." + - "12:00 P.M." + - "12 P.M." + - "12P.M." + - "12p.m." + +year_parse_style_1: + options: + YearParseStyle: 1 + sourceTime: !replace xxxx-05-xx xx:xx:xx + cases: + - target: !datedelta + sourceTime: !replace xxxx-02-11 xx:xx:xx + years: 1 + phrases: + - "February 11" + - "2/11" + - target: !replace xxxx-10-11 xx:xx:xx + phrases: + - "October 11" + - "10/11" + +year_parse_style_0: + options: + YearParseStyle: 0 + sourceTime: !replace xxxx-05-xx xx:xx:xx + cases: + - target: !replace xxxx-02-11 xx:xx:xx + phrases: + - "February 11" + - "2/11" + - target: !replace xxxx-10-11 xx:xx:xx + phrases: + - "October 11" + - "10/11" diff --git a/tests/data/en_US/simple_offsets.yml b/tests/data/en_US/simple_offsets.yml new file mode 100644 index 0000000..f99312d --- /dev/null +++ b/tests/data/en_US/simple_offsets.yml @@ -0,0 +1,255 @@ +constants: + - &tuesdays + - 2016-05-31 12:11:10 + - 2016-03-01 00:00:00 + - &default_day_start_time !replace xxxx-xx-xx 09:00:00 + +now: + cases: + - target: !datedelta + seconds: 0 + context: !pdtContext now + phrases: + - "now" + - "right now" + +offset_from_day_of_week: + sourceTime: *tuesdays + cases: + - target: !datedelta + days: 2 + context: !pdtContext day + phrases: + - "Thursday" + - target: !datedelta + # TODO: Handling this as Thursday at 9 plus 1 hour seems + # unexpected. + sourceTime: *default_day_start_time + days: 2 + hours: 1 + context: !pdtContext day | hour + phrases: + - "one hour from Thursday" + - "1h from Thursday" + - target: !datedelta + # TODO: Handling this as Thursday at 9 minus 1 hour seems + # unexpected. + sourceTime: *default_day_start_time + days: 2 + hours: -1 + context: !pdtContext day | hour + phrases: + - "an hour before Thursday" + - "1 hr before Thursday" + +offset_from_day_of_week_matching_source_time: + sourceTime: *tuesdays + options: + StartTimeFromSourceTime: True + cases: + - target: !datedelta + # TODO: Handling this as Thursday at the current time plus 1 hour + # seems unexpected. + days: 2 + hours: 1 + context: !pdtContext day | hour + phrases: + - "one hour from Thursday" + - target: !datedelta + # TODO: Handling this is Thursday at the current time minus 1 hour + # seems unexpected. + days: 2 + hours: -1 + context: !pdtContext day | hour + phrases: + - "one hour before Thursday" + +minutes_from_now: + cases: + - target: !datedelta + minutes: 5 + context: !pdtContext minutes | now + phrases: + - "5 minutes from now" + - "5 min from now" + - "5m from now" + - "five minutes from now" + - "five min from now" + - target: !datedelta + minutes: 5 + context: !pdtContext minutes + phrases: + - "in 5 minutes" + - "in 5 min" + - "5m" + - "in five minutes" + - "in five min" + - "five minutes" + - "five min" + +minutes_before_now: + cases: + - target: !datedelta + minutes: -5 + context: !pdtContext minutes | now + phrases: + - "5 minutes before now" + - "5 min before now" + - "5m before now" + - "five minutes before now" + - "five min before now" + - target: !datedelta + minutes: -5 + context: !pdtContext minutes + phrases: + - "5 minutes ago" + +week_from_now: + cases: + - target: !datedelta + weeks: 1 + context: !pdtContext weeks | now + phrases: + - "1 week from now" + - "one week from now" + - "a week from now" + - target: !datedelta + weeks: 1 + context: !pdtContext days | now + phrases: + - "7 days from now" + - "seven days from now" + - target: !datedelta + weeks: 1 + context: !pdtContext weeks + phrases: + - "in 1 week" + - "in one week" + - "in a week" + - target: !datedelta + sourceTime: *default_day_start_time + weeks: 1 + context: !pdtContext weeks + phrases: + - "next week" + - target: !datedelta + weeks: 1 + context: !pdtContext days + phrases: + - "in 7 days" + - "in seven days" + +week_before_now: + cases: + - target: !datedelta + weeks: -1 + context: !pdtContext weeks | now + phrases: + - "1 week before now" + - "one week before now" + - "a week before now" + - target: !datedelta + weeks: -1 + context: !pdtContext days | now + phrases: + - "7 days before now" + - "seven days before now" + - target: !datedelta + weeks: -1 + context: !pdtContext weeks + phrases: + - "1 week ago" + - "a week ago" + - target: !datedelta + sourceTime: *default_day_start_time + weeks: -1 + context: !pdtContext weeks + phrases: + - "last week" + +next_month: + cases: + - target: !datedelta + sourceTime: !replace xxxx-12-31 xx:xx:xx + years: 1 + context: !pdtContext month | day + phrases: + - "next December 31" + - sourceTime: !replace xxxx-02-03 xx:xx:xx + target: !datedelta + years: 1 + days: 1 + context: !pdtContext month | day + phrases: + - "next February 4" + # FIXME: February 29 is ignored even in leap years + # - sourceTime: !replace xxxx-02-27 xx:xx:xx + # target: !datedelta + # years: 1 + # days: 2 + # context: !pdtContext month | day + # phrases: + # - "next February 29" + +next_weekday: + sourceTime: *tuesdays + cases: + - target: !datedelta + sourceTime: *default_day_start_time + days: 10 + context: !pdtContext day + phrases: + - "next friday" + - "next friday?" + +next_weekday_matching_source_time: + options: + StartTimeFromSourceTime: True + sourceTime: *tuesdays + cases: + - target: !datedelta + days: 10 + context: !pdtContext day + phrases: + - "next friday" + +next_weekday_with_time: + sourceTime: *tuesdays + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 13:00:00 + days: 10 + context: !pdtContext day | hour + phrases: + - "next friday at 1pm" + - "1pm next friday" + sourceTime: *tuesdays + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-xx 13:00:00 + days: 3 + context: !pdtContext day | hour + phrases: + - "1pm this friday" + +specials: + cases: + - target: !datedelta + sourceTime: *default_day_start_time + days: 1 + context: !pdtContext days + phrases: + - "tomorrow" + - "next day" + - target: !datedelta + sourceTime: *default_day_start_time + days: -1 + context: !pdtContext days + phrases: + - "yesterday" + - target: !datedelta + sourceTime: *default_day_start_time + days: 0 + context: !pdtContext days + phrases: + - "today" diff --git a/tests/data/en_US/simple_offsets_hours.yml b/tests/data/en_US/simple_offsets_hours.yml new file mode 100644 index 0000000..5df2940 --- /dev/null +++ b/tests/data/en_US/simple_offsets_hours.yml @@ -0,0 +1,124 @@ +constants: + - &noon !replace xxxx-xx-xx 12:00:00 + +hours_from_now: + cases: + - target: !datedelta + hours: 5 + context: !pdtContext hour + phrases: + - "in 5 hours" + - "in 5 hour" + - "5 hours" + - "5 hr" + - "5h" + - "in five hours" + - "in five hour" + - "five hours" + - "five hr" + - target: !datedelta + hours: 5 + context: !pdtContext hour | now + phrases: + - "5 hours from now" + - "5 hour from now" + - "5 hr from now" + - "five hours from now" + - "five hour from now" + - "five hr from now" + - target: !datedelta + hours: 1 + context: !pdtContext hour + phrases: + - "in an hour" + - "an hour" + - "an hr" + - "an h" + - target: !datedelta + hours: 1 + context: !pdtContext hour | now + phrases: + - "an hour from now" + +invalid_hours_from_now: + cases: + - target: null + phrases: + - "anhour" + - "an hamburger" + +hours_before_now: + cases: + - target: !datedelta + hours: -5 + context: !pdtContext hour | now + phrases: + - "5 hours before now" + - "5 hr before now" + - "5h before now" + - "five hours before now" + - "five hr before now" + - target: !datedelta + hours: -1 + context: !pdtContext hour | now + phrases: + - "an hour before now" + - "an hr before now" + - "an h before now" + +hours_from_noon: + cases: + - target: !datedelta + sourceTime: *noon + hours: 5 + context: !pdtContext hour + phrases: + - "5 hours after 12pm" + - "five hours after 12pm" + - "5 hours after 12 pm" + - target: !datedelta + sourceTime: *noon + hours: 5 + context: !pdtContext hour | minute + phrases: + - "5 hours after 12:00pm" + - "5 hours after 12:00 pm" + - target: !datedelta + sourceTime: *noon + hours: 5 + context: !pdtContext hour | halfday + phrases: + - "5 hours after noon" + - "5 hours from noon" + +hours_before_noon: + cases: + - target: !datedelta + sourceTime: *noon + hours: -5 + context: !pdtContext hour + phrases: + - "5 hours before 12pm" + - "five hours before 12pm" + - "5 hours before 12 pm" + - target: !datedelta + sourceTime: *noon + hours: -5 + context: !pdtContext hour | minute + phrases: + - "5 hours before 12:00pm" + - "5 hours before 12:00 pm" + - target: !datedelta + sourceTime: *noon + hours: -5 + context: !pdtContext hour | halfday + phrases: + - "5 hours before noon" + # FIXME: This phrase does not respect the sourceTime; next noon is interpreted relative to the current date + # - target: !datedelta + # sourceTime: *noon + # days: 1 + # hours: -5 + # context: !pdtContext hour | halfday + # phrases: + # - "5 hours before next noon" diff --git a/tests/data/en_US/start_time_from_source_time.yml b/tests/data/en_US/start_time_from_source_time.yml new file mode 100644 index 0000000..e027bce --- /dev/null +++ b/tests/data/en_US/start_time_from_source_time.yml @@ -0,0 +1,37 @@ +end_of_phrases_enabled: + options: + StartTimeFromSourceTime: true + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-01 xx:xx:xx + months: 1 + days: -1 + context: !pdtContext day + phrases: + - "eom" + - "meeting eom" + - target: !datedelta + sourceTime: !replace xxxx-12-31 xx:xx:xx + context: !pdtContext month + phrases: + - "eoy" + - "meeting eoy" + +end_of_phrases_disabled: + options: + StartTimeFromSourceTime: false + cases: + - target: !datedelta + sourceTime: !replace xxxx-xx-01 09:00:00 + months: 1 + days: -1 + context: !pdtContext day + phrases: + - "eom" + - "meeting eom" + - target: !datedelta + sourceTime: !replace xxxx-12-31 09:00:00 + context: !pdtContext month + phrases: + - "eoy" + - "meeting eoy" diff --git a/tests/data/en_US/units.yml b/tests/data/en_US/units.yml new file mode 100644 index 0000000..e217aad --- /dev/null +++ b/tests/data/en_US/units.yml @@ -0,0 +1,127 @@ +minutes: + cases: + - target: !datedelta + minutes: 1 + context: !pdtContext minute + phrases: + - "1 minutes" + - "1 minute" + - "1 min" + - "1min" + - "1 m" + - "1m" + - "one minute" + - "one min" + - "one m" + - "a minute" + - target: !datedelta + minutes: -1 + context: !pdtContext minute + phrases: + - "1 minutes ago" + - "1 minute ago" + - "one minute ago" + - "one min ago" + - "one m ago" + - "a minute ago" + +hours: + cases: + - target: !datedelta + hours: 1 + context: !pdtContext hour + phrases: + - "1 hour" + - "1 hours" + - "1 hr" + - target: !datedelta + hours: -1 + context: !pdtContext hour + phrases: + - "1 hour ago" + - "1 hours ago" + +days: + cases: + - target: !datedelta + days: 1 + context: !pdtContext day + phrases: + - "1 day" + - "1 days" + - "1days" + - "1 dy" + - "1 d" + - target: !datedelta + days: -1 + context: !pdtContext day + phrases: + - "-1 day" + - "-1 days" + - "-1days" + - "-1 dy" + - "-1 d" + - "- 1 day" + - "- 1 days" + - "- 1days" + - "- 1 dy" + - "- 1 d" + - "1 day ago" + - "1 days ago" + +weeks: + cases: + - target: !datedelta + weeks: 1 + context: !pdtContext week + phrases: + - "1 week" + - "1week" + - "1 weeks" + - "1 wk" + - "1 w" + - "1w" + - target: !datedelta + weeks: -1 + context: !pdtContext week + phrases: + - "1 week ago" + - "1 weeks ago" + +months: + cases: + - target: !datedelta + months: 1 + context: !pdtContext month + phrases: + - "1 month" + - "1 months" + - "1month" + - target: !datedelta + months: -1 + context: !pdtContext month + phrases: + - "1 month ago" + - "1 months ago" + +years: + cases: + - target: !datedelta + years: 1 + context: !pdtContext year + phrases: + - "1 year" + - "1 years" + - "1 yr" + - "1 y" + - "1y" + - target: !datedelta + years: -1 + context: !pdtContext year + phrases: + - "1 year ago" + - "1 years ago" + - "1 yr ago" + - "1 y ago" + - "1y ago" + diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/lib/assertions.py b/tests/lib/assertions.py new file mode 100644 index 0000000..f465eb6 --- /dev/null +++ b/tests/lib/assertions.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" +Provides fixtures for custom assertions. + +In some cases the data returned by parsedatetime is not strictly equivalent; +these assertions provide some flexibility for assertions. +""" +from __future__ import unicode_literals + +import pytest +import time + + +@pytest.fixture +def assertLazyStructTimes(): + """A test fixture that compares `time.struct_time` values for cases where + parsedatetime does not properly update a few flags. + + The fixture provides a function that can be called to compare two + `time.struct_time` values, ignoring the ``wday``, ``yday``, and ``isdst`` + named fields. + + Example: + The fixture is loaded by requesting it as an argument of the test + function. Note that the function must be called with `time.struct_time` + values and will not unwrap tuples such as those returned by + `Calendar.parse` to find them:: + + @pdtFixture('names.yml') + def test_month_names(cal, phrase, sourceTime, target, context, assertLazyStructTimes): + result = cal.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + Returns: + Callable[[time.struct_time, time.struct_time], None]: A function that + can be used to compare two `time.struct_time` values. + + Raises: + AssertionError: if the `time.struct_time` values are not equivalent + after ignoring the specified fields. + """ + def doAssertLazyStructTimes(a, b): + assert ignoreWeekdayYeardayDST(a) == ignoreWeekdayYeardayDST(b) + return doAssertLazyStructTimes + + +def ignoreWeekdayYeardayDST(st): + return time.struct_time(st[:6] + (-1, -1, -1)) diff --git a/tests/lib/data.py b/tests/lib/data.py new file mode 100644 index 0000000..20ca864 --- /dev/null +++ b/tests/lib/data.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- +""" +Processes YAML data files with extensions specific to parsedatetime. + +In addition to the syntax supported by ``PyYaml``, this module provides a few +constructors to represent parsedatetime and test constructs in YAML. +""" + +import re +import yaml + +from parsedatetime.context import pdtContext +from tests.lib.models import datedelta, dateReplacement, nlpTarget +from tests import log + + +def loadData(path): + """Load YAML data from the specified path, memoizing the most recently + accessed file to avoid excessive IO. + + Args: + path (str): Absolute path to a YAML file. + + Returns: + Union[Dict, None]: Parsed contents of the file at `path` or `None` if + the file could not be read. + """ + data = None + + if path in loadData._cache: + data = loadData._cache[path] + else: + with open(path, 'r') as stream: + try: + data = yaml.load(stream) + # Purposely only keeping one in memory at a time + loadData._cache = { + path: data + } + except yaml.YAMLError as exc: + log.error(exc) + raise exc + + return data + + +loadData._cache = {} + + +def datedeltaConstructor(loader, node): + """A YAML constructor for representing time and date deltas relative to a source time. + + Unlike `datetime.timedelta`, this delta representation is interpreted + relative to a source time and can therefore represent calendar-based year + and month deltas via `Calendar.inc` in addition to the units supported by + `datetime.timedelta`. The following keys are supported: + + * months + * years + * days + * weeks + * hours + * minutes + * seconds + * milliseconds + * microseconds + * sourceTime + + Note that the ``sourceTime`` value should only be provided here if it + differs from the ``sourceTime`` assigned to the test case. For example, to + test that the phrase *4pm + 1d* works from a source time of *2016-02-28 + 00:00:00*, it would be necessary to include *2016-02-28 16:00:00* as the + ``sourceTime`` in the `datedelta` as shown in the examples below. While it + is generally recommended to specify targets as explicit `datetime.datetime` + values, in some cases this format makes the intent of the test more clear. + + Examples: + Yesterday:: + + !datedelta + days: -1 + + Next year (see `Calendar.inc`):: + + !datedelta + years: 1 + + 1 year, 2 months, and 30 minutes from now:: + + !datedelta + years: 1 + months: 2 + minutes: 30 + + 4pm + 1d:: + + !datedelta + sourceTime: 2016-02-28 16:00:00 + days: 1 + + Args: + loader (yaml.Loader): The YAML loader + node (yaml.MappingNode): The value provided to the ``!datedelta`` + constructor, unwrappable by `loader` as a dict + + Returns: + datedelta: A value that can be added to or subtracted from a + `datetime.datetime` to calculate an offset date. + + Raises: + TypeError: If any parameters are unsupported or of the wrong type. + """ + value = loader.construct_mapping(node) + return datedelta(**value) + + +def dateReplacementConstructor(loader, node): + """A YAML constructor for representing `dateReplacement` instances. + + In some cases it is necessary to replace a component of a + `datetime.datetime` in a way that cannot be done with `datedelta`. This + constructor provides a simple format for identifying portions of a datetime + to replace. + + Avoid creating impossible dates! February, April, June, September and + November should be avoided since they could result in an invalid date like + April 31. + + Examples: + To convert any date to January:: + + !replace xxxx-01-xx xx:xx:xx + + To set the hour and minute for any datetime:: + + !replace xxxx-xx-xx 12:34:xx + + Args: + loader (yaml.Loader): The YAML loader + node (yaml.ScalarNode): The value provided to the ``!replace`` + constructor, unwrappable by `loader` as a scalar value + + Returns: + dateReplacement: The `dateReplacement` corresponding to the scalar + value as returned by `dateReplacement.fromWildcardString`. + """ + value = loader.construct_scalar(node) + return dateReplacement.fromWildcardString(value) + + +def nlpTargetConstructor(loader, node): + """A YAML constructor for representing `nlpTarget` instances. + + Test cases expecting to be evaluated against `Calendar.nlp` can fully (or + partially, for convenience) specify the components of its return value in + the test data. The resulting `nlpTarget` can be compared against the tuple + returned by `nlp` with a simple equality assertion. + + The context and startIndex values are optional; providing a context + improves the quality of a test but startIndex *should only be used* if the + source phrase contains the same date phrase multiple times. Specifying a + startIndex reduces the reusability of tests cases (e.g. wrapping the phrase + in quotes). + + Examples: + Yesterday I went to the park at noon:: + + !nlpTarget + - phrase: "Yesterday" + context: !pdtContext day + target: !datedelta + days: -1 + - phrase: "at noon" + context: !pdtContext halfday + target: 2016-01-01 12:00:00 + + Yep, today was as good as today could be:: + + !nlpTarget + - phrase: "today" + startIndex: 5 + target: 2013-08-01 09:00:00 + - phrase: "today" + startIndex: 26 + target: 2013-08-01 09:00:00 + + Args: + loader (yaml.Loader): The YAML loader + node (yaml.SequenceNode): The value provided to the ``!nlpTarget`` + constructor, unwrappable by `loader` as a sequence value + + Returns: + nlpTarget: A value that can be compared against the return value of + `nlp`. + + Raises: + TypeError: If any data is of the wrong type. + """ + values = loader.construct_sequence(node, deep=True) + return nlpTarget(values) + + +def pdtContextConstructor(loader, node): + """A YAML constructor for representing `pdtContext` instances. + + `pdtContext` tracks the composition of a date phrase based on various + components such as month, year, and hour. The accuracy can be specified in + YAML by separating mulitple values with the pipe ``|`` character, + reminiscent of the bitwise OR used to combine accuracy flags in python. + Any value in `pdtContext._ACCURACY_REVERSE_MAPPING` may be used (or no + value at all), though singular forms are preferred over plurals for + consistency. + + Examples: + 2 days ago:: + + !pdtContext day + + 2 months, 1 hour, 4 minutes, 32 seconds:: + + !pdtContext second | hour | month | minute + + Order does not matter, but to maintain consistency the flags should be + listed in decreasing duration order:: + + !pdtContext month | hour | minute | second + + Noon:: + + !pdtContext halfday + + No date or time components:: + + !pdtContext + + Args: + loader (yaml.Loader): The YAML loader + node (yaml.ScalarNode): The value provided to the ``!pdtContext`` + constructor, unwrappable by `loader` as a scalar value + + Returns: + pdtContext: A context representing the specified accuracy values + """ + value = loader.construct_scalar(node) + accuracyStrings = re.split(r'\s*\|\s*', value) + return pdtContext.fromAccuracyStrings(accuracyStrings) + + +yaml.add_constructor(u'!datedelta', datedeltaConstructor) +yaml.add_constructor(u'!replace', dateReplacementConstructor) +yaml.add_constructor(u'!nlpTarget', nlpTargetConstructor) +yaml.add_constructor(u'!pdtContext', pdtContextConstructor) diff --git a/tests/lib/fixtures.py b/tests/lib/fixtures.py new file mode 100644 index 0000000..7125b86 --- /dev/null +++ b/tests/lib/fixtures.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +""" +Provides support for loading test data from files and preparing it for +convenient testing. + +The primary interface used for testing is the `pdtFixture` function. +""" +from __future__ import unicode_literals + +import glob +import inspect +import os +import pytest + +from parsedatetime import Calendar +from tests.lib.data import loadData +from tests.lib.models import TestGroup +from tests import log + + +@pytest.fixture +def calendar(): + """A pytest fixture that provides a calendar with the default configuration + for use in tests that do not make use of `pdtFixture`. + + Returns: + parsedatetime.Calendar: A calendar with the default configuration. + """ + return Calendar() + + +def pdtFixture(filename, explicitTestGroupNames=None, localeIDs=None): + """A decorator for test functions that uses data from the specified file + to parametrize the test. + + The `pdtFixture` decorator supports the following arguments based on the + test data: + + calendar + A `Calendar` preconfigured according to the test data and locale. + phrase + The string to be tested. + sourceTime + One or more `datetime.datetime` values against which to run the test. + If not provided, the test will be parametrized against several common + edge case dates. A `dateReplace` can be used here to modify the default + edge case dates, for example to ensure that the month is January so + that dates in February are interpreted within the same year rather than + the following year. + target + The target value specified in the test, usually a `datetime.datetime` + or `nlpTarget`. When test data targets a `datedelta`, `timedelta`, or + `dateReplace`, ``target`` will be resolved to a `datetime.datetime` + relative to `sourceTime`. The ``target`` cannot be omitted; tests + intentionally targeting invalid cases must explicitly specify ``target: + null``. + context + The `pdtContext` that should represent the date and time components + within the phrase. + + Examples: + Tests are automatically parametrized across all locales with data from + the test group matching the test name in the specified data file:: + + @pdtFixture('deltas.yml') + def test_past_float_deltas(calendar, phrase, sourceTime, target, context): + assert cal.parse(phrase, sourceTime) == (target, context) + + This will load the *past_float_deltas* test group from ``deltas.yml`` + then parametrize the function with `@pytest.mark.parametrize` for all + phrases in all cases in the test group. All locales that include a + ``deltas.yml`` with a *past_float_deltas* test group will be included + in this test. + + Since parametrization allows each python test function to run against + many different combinations of parameters it is important to understand + that a single python test function may emit multiple successes or + failures. + + Tests may span multiple test groups:: + + @pdtFixture('deltas.yml', ['past_int_deltas', 'past_float_deltas']) + def test_deltas_wrapped_in_quotes(calendar, phrase, sourceTime, target, context): + assert calendar.parse(u'"%s"' % phrase, sourceTime) == (target, context) + + While generally discouraged, in some cases it may be necessary to + restrict tests to specific locales:: + + @pdtFixture('deltas.yml', ['simple_multiple_unit_deltas'], ['en_US', 'en_AU']) + def test_multiunit_deltas_with_from_today_suffix(calendar, phrase, sourceTime, target, context): + assert calendar.parse(u'%s from today' % phrase, sourceTime) == (target, context) + + It is also possible to combine `pdtContext` with + ``@pytest.mark.parametrize`` for additional test combinations:: + + @pytest.mark.parametrize('prefix,suffix', [('"','"'), ('(',')'), ('[',']')]) + @pdtFixture('deltas.yml') + def test_past_float_deltas(calendar, phrase, sourceTime, target, context, prefix, suffix): + assert calendar.parse(u'%s%s%s' % (prefix, phrase, suffix), sourceTime) == (target, context) + + In this test, every phrase from `pdtContext` will be tested against + every combination of parameters in ``@pytest.mark.parametrize``. + + Args: + filename (str): The name of the file from which to load test data + relative to the locale directory. The file will be loaded from + :file:`./tests/data/{localeID}/{filename}` and must include the + proper extension. + explicitTestGroupNames (Optional[List[str]]): The specific test + groups to load from the specified fixture data file. If + unspecified, `pdtFixture` will look for a test group matching the + python test function name. Wildcards are not supported to + ensure consistent behavior. + localeIDs (Optional[List[str]]): A list of locale + identifiers supported by `pdt_locales`, or ``None`` to load + corresponding test data from all locales. Defaults to ``None``. + + Returns: + Callable[[Callable], Callable]: A decorator for parametrizing test + functions. + + The decorator populates the parameters required by the test function + according to the arguments provided to `pdtFixture` then passes them + along to ``@pytest.mark.parametrize``. The test function will be run + for each set of parameters. Additional test combinations can be + achieved by marking the test case with ``@pytest.mark.parametrize``. + For more detail, see `the pytest documentation on parametrization + <@pytest.mark.parametrize>`. + """ + def decorator(testFn): + parameters = inspect.getargspec(testFn)[0] + basedir = os.path.dirname(inspect.getsourcefile(testFn)) + testGroupNames = explicitTestGroupNames or \ + [testFn.__name__.replace('test_', '')] + parameterization = generateParameters(filename, basedir, testGroupNames, + parameters, localeIDs) + + return pytest.mark.parametrize(*parameterization)(testFn) + return decorator + + +def generateParameters(filename, basedir, testGroupNames, parameters, + localeIDs=None): + """Collects all parameters from all test groups and returns a tuple that + can be passed as ``*args`` to `@pytest.mark.parametrize`. + + Args: + filename (str): The name of the test data file, including the ``.yml`` + extension. + basedir (str): The directory of the test file, relative to which the + data will be loaded. + testGroupNames (List[str]): The test groups from the specified file + that should be included in the parametrization. + parameters (List[str]): The parameter names required by the test + function. + localeIDs (Optional[List[str]]): A list of locale identifiers supported + by `pdt_locales`, or ``None`` to load corresponding test data from + all locales. Defaults to ``None``. + + Returns: + Tuple[str, List[Tuple]: A tuple that can be passed as ``*args`` to + `@pytest.mark.parametrize`. + + The first element in the tuple is a string representing the arguments + that will be provided for the testing function. The order corresponds + to the order of values in all of the tuples in the second element. + """ + localeID = localeIDs[0] if localeIDs and len(localeIDs) == 1 else '*' + paths = glob.iglob(os.path.join(basedir, 'data', localeID, filename)) + knownParameters = TestGroup.supportedParameters(parameters) + parameterGroups = [] + + for path in paths: + localeID = os.path.basename(os.path.dirname(path)) + + if localeIDs and localeID not in localeIDs: + continue + + allGroups = loadData(path) + + if not allGroups: + continue + + testGroups = [group for groupName, group in allGroups.items() + if groupName in testGroupNames] + unsupportedTests = [groupName for groupName in testGroupNames + if groupName not in allGroups] + + # TODO: summary after tests of any unsupported cases + for unsupportedTest in unsupportedTests: + log.warn('Locale %s does not provide test %s', localeID, + unsupportedTest) + + for group in testGroups: + group = TestGroup(group, localeID) + parameterGroups += group.parameterValues(knownParameters) + + return (','.join(knownParameters), parameterGroups) diff --git a/tests/lib/models.py b/tests/lib/models.py new file mode 100644 index 0000000..2c49358 --- /dev/null +++ b/tests/lib/models.py @@ -0,0 +1,616 @@ +# -*- coding: utf-8 -*- +""" +Convenience classes for testing parsedatetime. + +Allows test data to be represented flexibly in the YAML data files with minimal +logic required in each test assertion. +""" + +from datetime import datetime, timedelta +import re + +from parsedatetime import Calendar, Constants, VERSION_CONTEXT_STYLE +from parsedatetime.context import pdtContext + + +# Allow Python 2 type checking in Python 3 +try: + unicode = unicode +except NameError: # pragma: no cover + basestring = (str, bytes) + long = int + + +DEFAULT_SOURCE_TIMES = ( + datetime(2016, 2, 29, 3, 4, 5), + datetime(2015, 2, 28, 23, 22, 21), + datetime(1945, 12, 31, 3, 4, 5), +) + +_PROPERTY_MAPPING = { + 'sourceTime': lambda case, target, phrase: case.sourceTime, + 'target': lambda case, target, phrase: case.resolveTarget(target, phrase), + 'phrase': lambda case, target, phrase: phrase, + 'context': lambda case, target, phrase: case.context, + 'calendar': lambda case, target, phrase: case.calendar, + 'nlpTarget': lambda case, target, phrase: case.nlpTarget(target, phrase), + 'case': lambda case, target, phrase: case, +} + + +class datedelta(object): + """Represents a change in time over a number of years, months, or any unit + supported by `datetime.timedelta`. + + Months are added or subtracted maintaining the date within the month. If + the date is greater than the number of days in the month (e.g. moving from + January 31 to February) the date is adjusted to the last day of the target + month. + + Similarly, when a change in year produces an invalid date (e.g. Feb 29, + 2016 to 2017) the date is adjusted to the last day of the month. + + Months and years are added *before* the ``timedelta`` is applied. For + example, adding 1 month to January 30 yields February 28 then adding one + day yields March 1. If the ``timedelta`` were applied first, 1 day would be + added to January 30 yielding January 31, then one month would be added + yielding February 28. + """ + + def __init__(self, years=0, months=0, sourceTime=None, **kwargs): + """Initialize a `datedelta` + + Args: + years (Optional[int]): Number of years. + months (Optional[int]): Number of months. + sourceTime (Optional[Union[datetime.datetime, dateReplacement]]): + The date relative to which this `datedelta` should be + interpreted. + calendar (Optional[parsedatetime.Calendar]): The calendar on which + `Calendar.inc` will be called to handle year and month + calculation. + **kwargs: Keyed arguments acceptable to `datetime.timedelta`. + + Raises: + TypeError: If any arguments are of the wrong type or if the + ``**kwargs`` contain keys not supported by + `datetime.timedelta`. + """ + delta = timedelta(**kwargs) + typeErrorMsg = 'unsupported type for datedelta %s component: %s' + + if not isinstance(years, (int, long)): + raise TypeError(typeErrorMsg % ('years', type(years).__name__)) + if not isinstance(months, (int, long)): + raise TypeError(typeErrorMsg % ('months', type(months).__name__)) + if not isinstance(sourceTime, (datetime, dateReplacement, type(None))): + raise TypeError('unsupported type for datedelta %s: %s' % + ('sourceTime', type(sourceTime).__name__)) + + self._years = years + self._months = months + self._sourceTime = sourceTime + self._kwargs = kwargs + self._timedelta = delta + + @property + def years(self): + """int: Number of years to offset a date.""" + return self._years + + @property + def months(self): + """int: Number of months to offset a date.""" + return self._months + + @property + def sourceTime(self): + """Union[datetime.datetime,dateReplacement]: The date relative to which + this `datedelta` should always be interpreted. It is expected that any + logic operating on a `datedelta` will check whether the `datedelta` has + a `sourceTime` and if so, calculate a final date based on that rather + than some other time. + """ + return self._sourceTime + + def add(self, other, calendar): + """Add the datedelta to the specified date. + + Args: + other (datetime.datetime): The ``datetime`` to which the interval + represented by this ``datedelta`` should be added. + calendar (Calendar): The calendar responsible for year and month + addition. + + Returns: + datetime.datetime: The date adjusted according to the ``datedelta`` + """ + if isinstance(other, datetime): + return (calendar.inc(other, self._months, self._years) + + self._timedelta) + raise NotImplementedError + + def __repr__(self): + return "%s(%d, %d, %s)" % (self.__class__.__name__, + self._years, + self._months, + repr(self._timedelta)) + + def __str__(self): + def plural(n): + return n, abs(n) != 1 and "s" or "" + s = [] + if self._years: + s.append('%d year%s' % plural(self._years)) + if self._months: + s.append('%d month%s' % plural(self._months)) + + s.append(str(self._timedelta)) + return ', '.join(s) + + +class dateReplacement(object): + """A wrapper for `datetime.datetime.replace` allowing arbitrary + modifications to individual fields in a `datetime.datetime`. + """ + def __init__(self, **kwargs): + """Initializes a `dateReplacement` with arguments suitable for + `datetime.datetime.replace`. + + Args: + **kwargs: Arguments for `datetime.datetime.replace`. + + Raises: + TypeError: If the keyword arguments are not acceptable for + `datetime.datetime.replace`. + """ + # Use datetime.replace for its assertions + datetime.now().replace(**kwargs) + self._kwargs = kwargs + + def replace(self, sourceTime): + """Perform the replacement on the given ``datetime``. + + Args: + sourceTime (datetime.datetime): The date to use as a source for + replacement. + + Returns: + datetime.datetime: A copy of the ``sourceTime`` modified according + to the replacement rules. + """ + return sourceTime.replace(**self._kwargs) + + @classmethod + def fromWildcardString(cls, wildcardString): + """Converts a string in *yyyy-mm-dd HH:MM:SS* format to a + `dateReplacement`. + + Any field that should not be modified should be marked with an ``x`` + and any field denoted with a number will be used for replacement. For + example, to convert a date to January without affecting the year, day, + or time the ``wildcardString`` should be ``xxxx-01-xx xx:xx:xx``. A + field cannot be partially replaced; ``19xx-xx-xx xx:xx:xx`` will make + no changes to the ``sourceTime`` since the year was not fully + specified. + + Args: + wildcardString (str): A string in *yyyy-mm-dd HH:MM:SS* format with + numbers in any field that should be replaced. + + Returns: + dateReplacement: The `dateReplacement` instance capable of making + the replacement identified in the `wildcardString`. + + Raises: + ValueError: If the `wildcardString` is not properly formatted. + """ + components = re.split(r'[:-]| +', wildcardString) + keywords = ('year', 'month', 'day', 'hour', 'minute', 'second') + kwargs = {} + + if len(components) != 6: + raise ValueError('invalid dateReplacement string: %s' % + wildcardString) + + for index, kw in enumerate(keywords): + if components[index].isdigit(): + kwargs[kw] = int(components[index]) + + return dateReplacement(**kwargs) + + def __eq__(self, other): + if isinstance(other, dateReplacement): + return self._kwargs == other._kwargs + return NotImplemented + + +class nlpTarget(object): + """Represents one or more dates and phrases that would be parsed from a + source phrase by `nlp`. + """ + def __init__(self, targets, testCase=None, sourcePhrase=None): + """Initializes an `nlpTarget`. + + Args: + targets (List[Dict[str,Any]]): A list of dictionaries with keys + corresponding to keyword arguments of `nlpTargetValue`. There + may be no additional keys and all input is type-validated with + assertions. + sourcePhrase (str): The phrase on which `nlp` will operate. + testCase (TestCase): The `TestCase` to use for resolving targets. + + Raises: + AssertionError: If any input is of the incorrect type. + """ + self._targets = targets + self._targetValues = [nlpTargetValue(**target) for target in targets] + self._testCase = testCase + self.sourcePhrase = sourcePhrase + + def forTestCase(self, testCase, sourcePhrase): + return nlpTarget(self._targets, testCase, sourcePhrase) + + @property + def testCase(self): + return self._testCase + + @property + def sourcePhrase(self): + return self._sourcePhrase + + @sourcePhrase.setter + def sourcePhrase(self, sourcePhrase): + self._sourcePhrase = sourcePhrase + + @property + def tupleValue(self): + """Union[None,Tuple[Tuple[datetime.datetime,pdtContext,int,int,str]]]: + The expected result from ``calendar.nlp(sourcePhrase, sourceTime)`` + + Raises: + ValueError: If `testCase` or `sourcePhrase` are not yet set. + ValueError: If any of the target values include a phrase that is + not contained in `sourcePhrase`. + """ + values = [] + + if self.testCase is None: + raise ValueError('testCase must be specified to generate a ' + + 'tupleValue') + if self.sourcePhrase is None: + raise ValueError('sourcePhrase must be specified to generate a ' + + 'tupleValue') + + for targetValue in self._targetValues: + if targetValue.target is None: + continue + startIndex = targetValue.startIndex + target = self.testCase.resolveTarget(targetValue.target) + if startIndex is None: + if targetValue.phrase not in self.sourcePhrase: + raise ValueError('The phrase %r is not contained in %r' % ( + targetValue.phrase, self.sourcePhrase)) + startIndex = self.sourcePhrase.index(targetValue.phrase) + endIndex = startIndex + len(targetValue.phrase) + values.append((target, targetValue.context, startIndex, endIndex, + targetValue.phrase)) + + return tuple(values) or None + + def __repr__(self): + return '%s%s' % (self.__class__.__name__, self.tupleValue) + + def __eq__(self, other): + """Allows an `nlpTarget` to be compared directly to the tuple returned + by `nlp`. + + Args: + other (Union[tuple, nlpTarget]): The object to test for equality. + + Returns: + bool: Whether the objects compared both represent the same value. + """ + if other is None: + return self.tupleValue is None + if isinstance(other, tuple): + return self.tupleValue == other + if isinstance(other, nlpTarget): + return self.tupleValue == other.tupleValue + return NotImplemented + + +class nlpTargetValue(object): + """An immutable container structure for representing a single value in the + tuple format created by `nlp`. + """ + def __init__(self, phrase, target, context=None, startIndex=None): + """Initializes an `nlpTargetValue` + + Args: + phrase (str): The phrase matched by `nlp`. + target (Union[datetime.datetime,datedelta]): The date represented + by the phrase. + context (Optional[pdtContext]): The context representing the + phrase. If not specified, the wildcard context will be used to + match any context. + startIndex (Optional[int]): The index of the phrase as it appears + in the source phrase. The end index is always calculated + automatically based on the length of the phrase. + """ + if not isinstance(phrase, basestring): + raise TypeError('unsupported type for nlpTargetValue phrase' % + type(phrase).__name__) + if not isinstance(context, (pdtContext, type(None))): + raise TypeError('unsupported type for nlpTargetValue context: %s' % + type(context).__name__) + if not isinstance(startIndex, (int, long, type(None))): + raise TypeError('unsupported type for nlpTargetValue startIndex' % + type(startIndex).__name__) + + self._phrase = phrase + self._target = target + self._context = context or pdtContext(pdtContext.ACU_WILDCARD) + self._startIndex = startIndex + + @property + def phrase(self): + return self._phrase + + @property + def target(self): + return self._target + + @property + def context(self): + return self._context + + @property + def startIndex(self): + return self._startIndex + + +class TestGroup(object): + """Parses a test group configuration to prepare for parametrizing a test. + """ + def __init__(self, groupData, localeID): + """Initializes a `TestGroup`. `TestCase` instances are not created + until the `parameterValues` are requested. + + Args: + groupData (Dict): The data for a group of related tests. Supported + top-level keys are: + + sourceTime + An optional way to specify or modify the datetimes against + which the test will be run. By default, the test will be + run against a small set of dates focusing on leap days and + similar edge cases. Specify a `dateReplacement` to modify + those dates, for example to ensure the month is January. A + list of dates can be provided as an alternative to the + default dates, which is particularly useful when dates must + fall on a specific day of the week. While a single + `datetime.datetime` can be provided, it is recommended to + specify at least two values for ``sourceTime`` if the + defaults cannot be used. + options + A dictionary mapping `Constants` attribute names to the + value that should be set when running a test. This is + useful for modifying flags such as StartTimeFromSourceTime + or DOWParseStyle. + cases + A list of objects suitable for creating `TestCase`. Cases + are usually grouped by target and context so that multiple + phrases with the same target date and context flags appear + in a single case. + localeID (str): The locale from which the test data was loaded. + """ + # TODO: Constants and Calendar options in test data + constants = Constants(localeID, usePyICU=False) + sourceTime = groupData.get('sourceTime') + options = groupData.get('options') or {} + calendar_options = {} + + for (attr, value) in options.items(): + if attr == 'day_start_hour': + calendar_options[attr] = value + else: + setattr(constants, attr, value) + + self._calendar = Calendar(constants, version=VERSION_CONTEXT_STYLE, **calendar_options) + self._caseData = groupData['cases'] + self._sourceTimes = DEFAULT_SOURCE_TIMES + + if isinstance(sourceTime, dateReplacement): + self._sourceTimes = [sourceTime.replace(dt) + for dt in self._sourceTimes] + elif isinstance(sourceTime, list): + self._sourceTimes = sourceTime + elif sourceTime is not None: + self._sourceTimes = [sourceTime] + + @property + def calendar(self): + return self._calendar + + @property + def sourceTimes(self): + return self._sourceTimes + + @classmethod + def supportedParameters(self, parameters): + """Filters the given parameters returning a list of those supported by + a test group. + + Args: + parameters (List[str]): A list of function parameter names, some of + which may be pytest fixtures, others may be provided by + testGroup parametrization. + + Returns: + List[str]: The parameters that are supported by testGroup + parametrization. + """ + return [p for p in parameters if p in _PROPERTY_MAPPING] + + def parameterValues(self, parameters): + """Gather all values for the specified parameters across all cases in + the test group. + + Args: + parameters (List[str]): The parameters that will be injected into + the test function. Supported parameters are ``sourceTime``, + ``target``, ``phrase``, ``context``, ``calendar``, and + ``nlpTarget``. + + Returns: + List[Tuple]: A list of tuples containing values that correspond to + the order of the `parameters`. + """ + values = [] + for sourceTime in self.sourceTimes: + for caseData in self._caseData: + case = TestCase(caseData, self.calendar, sourceTime) + values.extend(case.parameterValues(parameters)) + + return values + + +class TestCase(object): + """Represents a single test case within a group, resolving test data + according to the group and case configuration. + """ + def __init__(self, caseData, calendar, sourceTime): + self._calendar = calendar + self._sourceTime = sourceTime + self._phrases = caseData['phrases'] + self._target = caseData.get('target') + self._context = (caseData.get('context') or + pdtContext(pdtContext.ACU_WILDCARD)) + + if 'sourceTime' in caseData: + self._sourceTime = self.resolveTime(caseData['sourceTime']) + + @property + def calendar(self): + return self._calendar + + @property + def context(self): + return self._context + + @property + def sourceTime(self): + return self._sourceTime + + @property + def targetByPhrase(self): + """Resolves the target value for each phrase. + + Returns: + Dict[str, Any]: A dictionary mapping phrases to the target + specified in the test data. In most cases the target is either a + `datetime.datetime` or ``None``. + """ + phrases = {} + if isinstance(self._phrases, list): + for phrase in self._phrases: + target = self.resolveTarget(self._target, phrase) + phrases[phrase] = target + if isinstance(self._phrases, dict): + for phrase, target in self._phrases.items(): + phrases[phrase] = self.resolveTarget(target, phrase) + return phrases + + def resolveTime(self, value): + """Uses the case's `sourceTime` to resolve date replacements and + deltas to explicit `datetime.datetime` values. + + Args: + value (Union[datetime.datetime, dateReplacement, datedelta]): + The value to resolve. + + Returns: + datetime.datetime: The explicit `datetime.datetime` to which the + `value` mapped in the context of the `sourceTime`. + """ + if isinstance(value, datetime): + return value + if isinstance(value, dateReplacement): + return value.replace(self.sourceTime) + if isinstance(value, datedelta): + sourceTime = self.resolveTime(value.sourceTime) + return value.add(sourceTime, self.calendar) + return self.sourceTime + + def resolveTarget(self, value, phrase=None): + """Ensures that the target is ready for use in a test assertion. + + Resolves `dateReplacement` and `datedelta` to an explicit + `datetime.datetime` and prepares `nlpTarget` values for comparison to + `nlp` return values. + + Args: + value (Any): The value to resolve. + phrase (Optional[str]): The phrase with which the target is + associated for use with resolving `nlpTarget` values. + + Returns: + Any: The resolved value, or the provided value if not of a type + that requires resolution. + """ + if isinstance(value, (dateReplacement, datedelta)): + return self.resolveTime(value) + if isinstance(value, list): + return [self.resolveTarget(item, phrase) for item in value] + if isinstance(value, nlpTarget): + return value.forTestCase(self, phrase) + return value + + def nlpTarget(self, value, phrase): + """Coerces the `value` to an `nlpTarget` then resolves that target to + prepare for testing. + + Args: + value (Any): A test target value. + phrase (str): The phrase with which the target is associated. + + Returns: + nlpTarget: An `nlpTarget` that is prepared to compare against the + return value of `nlp`. + """ + if not isinstance(value, nlpTarget): + value = nlpTarget(targets=[{ + 'target': value, + 'context': self.context, + 'phrase': phrase + }]) + return self.resolveTarget(value, phrase) + + def parameterValues(self, parameters): + """Collects the values corresponding to each parameter from the test + data. + + Args: + parameters (List[str]): The parameters that will be injected into + the test function. + + Returns: + Tuple: A tuple containing values that correspond to the order of + the `parameters`. + """ + values = [] + for phrase, target in self.targetByPhrase.items(): + phraseValues = [] + for parameter in parameters: + if parameter in _PROPERTY_MAPPING: + value = _PROPERTY_MAPPING[parameter](self, target, phrase) + phraseValues.append(value) + # Individual parameters must be unwrapped otherwise a tuple + # containing the value is injected in the test + if len(phraseValues) == 1: + values.append(phraseValues[0]) + else: + values.append(tuple(phraseValues)) + + return tuple(values) diff --git a/tests/lib/tests/data/en_US/constructors.yml b/tests/lib/tests/data/en_US/constructors.yml new file mode 100644 index 0000000..5e22188 --- /dev/null +++ b/tests/lib/tests/data/en_US/constructors.yml @@ -0,0 +1,7 @@ +datedelta: !datedelta + years: 1 + days: 1 + +dateReplacement: !replace xxxx-01-xx 01:xx:xx + +pdtContext: !pdtContext year | hour | second \ No newline at end of file diff --git a/tests/lib/tests/data/en_US/empty.yml b/tests/lib/tests/data/en_US/empty.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/lib/tests/data/en_US/nlpTarget.yml b/tests/lib/tests/data/en_US/nlpTarget.yml new file mode 100644 index 0000000..5e22188 --- /dev/null +++ b/tests/lib/tests/data/en_US/nlpTarget.yml @@ -0,0 +1,7 @@ +datedelta: !datedelta + years: 1 + days: 1 + +dateReplacement: !replace xxxx-01-xx 01:xx:xx + +pdtContext: !pdtContext year | hour | second \ No newline at end of file diff --git a/tests/lib/tests/data/en_US/simple.yml b/tests/lib/tests/data/en_US/simple.yml new file mode 100644 index 0000000..aff7b09 --- /dev/null +++ b/tests/lib/tests/data/en_US/simple.yml @@ -0,0 +1,13 @@ +noon: + sourceTime: 2016-01-01 08:00:00 + cases: + - target: 2016-01-01 12:00:00 + phrases: + - "noon" + +morning: + sourceTime: 2016-01-01 08:00:00 + cases: + - target: 2016-01-01 06:00:00 + phrases: + - "morning" diff --git a/tests/lib/tests/data/en_US/targets.yml b/tests/lib/tests/data/en_US/targets.yml new file mode 100644 index 0000000..bcd2818 --- /dev/null +++ b/tests/lib/tests/data/en_US/targets.yml @@ -0,0 +1,11 @@ +target_by_phrase: + cases: + - phrases: + noon: !replace xxxx-xx-xx 12:00:00 + morning: !replace xxxx-xx-xx 06:00:00 + # NOTE: specifying an absolute sourceTime here is not encouraged since + # the test will still run once for each sourceTime assigned to the group + - sourceTime: 2016-01-12 00:00:00 + phrases: + "Jan 12, 2016 5am": 2016-01-12 05:00:00 + \ No newline at end of file diff --git a/tests/lib/tests/data/en_US/yaml_error.yml b/tests/lib/tests/data/en_US/yaml_error.yml new file mode 100644 index 0000000..22ded55 --- /dev/null +++ b/tests/lib/tests/data/en_US/yaml_error.yml @@ -0,0 +1 @@ +: \ No newline at end of file diff --git a/tests/lib/tests/test_TestCase.py b/tests/lib/tests/test_TestCase.py new file mode 100644 index 0000000..754afa7 --- /dev/null +++ b/tests/lib/tests/test_TestCase.py @@ -0,0 +1,17 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('targets.yml') +def test_target_by_phrase(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple.yml', explicitTestGroupNames=['noon']) +def test_unknown_parameters(phrase, target, case): + assert case.parameterValues(['unknown']) == ((),) + + +@pdtFixture('simple.yml', explicitTestGroupNames=['noon']) +def test_single_parameter(phrase): + assert phrase == 'noon' \ No newline at end of file diff --git a/tests/lib/tests/test_dateReplacement.py b/tests/lib/tests/test_dateReplacement.py new file mode 100644 index 0000000..c36553f --- /dev/null +++ b/tests/lib/tests/test_dateReplacement.py @@ -0,0 +1,82 @@ +from datetime import datetime +import pytest + +from tests.lib.models import dateReplacement + + +def test_replace(): + replacement = dateReplacement( + year=1, + month=1, + day=1, + hour=1, + minute=1, + second=1, + ) + source = datetime.now() + target = datetime(1, 1, 1, 1, 1, 1, source.microsecond) + assert replacement.replace(source) == target + + +def test_wildcard_string(): + wildcard = '1111-12-13 14:15:16' + target = dateReplacement( + year=1111, + month=12, + day=13, + hour=14, + minute=15, + second=16, + ) + assert dateReplacement.fromWildcardString(wildcard) == target + + +def test_wildcard_string_supports_omission_of_leading_zero(): + wildcard = '1-2-3 4:5:6' + target = dateReplacement( + year=1, + month=2, + day=3, + hour=4, + minute=5, + second=6, + ) + assert dateReplacement.fromWildcardString(wildcard) == target + + +def test_wildcard_string_ignores_partial_replacements(): + wildcard = '12xx-1x-1x 1x:1x:1x' + target = dateReplacement() + assert dateReplacement.fromWildcardString(wildcard) == target + + +def test_raises_error_on_invalid_wildcard_strings(): + wildcard = '1111-12-13 14:15:16:xx' + with pytest.raises(ValueError): + dateReplacement.fromWildcardString(wildcard) + + wildcard = '1111-12-13' + with pytest.raises(ValueError): + dateReplacement.fromWildcardString(wildcard) + + +def test_raises_error_on_invalid_kwargs(): + with pytest.raises(TypeError): + dateReplacement(foo=1) + + +def test_equality_to_other_types(): + assert not (dateReplacement() == 1) + + +def test_accepts_all_datetime_replace_kwargs(): + dateReplacement( + year=1, + month=1, + day=1, + hour=1, + minute=1, + second=1, + microsecond=1, + tzinfo=None, + ) diff --git a/tests/lib/tests/test_datedelta.py b/tests/lib/tests/test_datedelta.py new file mode 100644 index 0000000..a931768 --- /dev/null +++ b/tests/lib/tests/test_datedelta.py @@ -0,0 +1,91 @@ +from datetime import datetime +import pytest + +from tests.lib.models import datedelta + + +def test_get_years(calendar): + target = 2 + delta = datedelta(years=target) + assert delta.years == target + + +def test_get_months(calendar): + target = 2 + delta = datedelta(months=target) + assert delta.months == target + + +def test_raises_error_on_invalid_types(calendar): + with pytest.raises(TypeError): + datedelta(years='bad') + + with pytest.raises(TypeError): + datedelta(months={}) + + with pytest.raises(TypeError): + datedelta(sourceTime='bad') + + +def test_add_years(calendar): + delta = datedelta(years=2) + start = datetime(2017, 1, 2) + target = datetime(2019, 1, 2) + assert delta.add(start, calendar) == target + + delta = datedelta(years=1) + start = datetime(2016, 2, 29) + target = datetime(2017, 2, 28) + assert delta.add(start, calendar) == target + + +def test_add_months(calendar): + delta = datedelta(months=2) + start = datetime(2017, 1, 2) + target = datetime(2017, 3, 2) + assert delta.add(start, calendar) == target + + delta = datedelta(months=1) + start = datetime(2017, 1, 31) + target = datetime(2017, 2, 28) + assert delta.add(start, calendar) == target + + delta = datedelta(months=-1) + start = datetime(2016, 3, 31) + target = datetime(2016, 2, 29) + assert delta.add(start, calendar) == target + + +def test_add_timedelta(calendar): + delta = datedelta(days=1, hours=1) + start = datetime(2017, 1, 2, 9, 0, 0) + target = datetime(2017, 1, 3, 10, 0, 0) + assert delta.add(start, calendar) == target + + +def test_can_only_add_to_datetime(calendar): + delta = datedelta(years=2) + with pytest.raises(NotImplementedError): + delta.add('not a datetime', calendar) + + +def test_string_representation(): + cases = ( + (datedelta(years=1), '1 year, 0:00:00'), + (datedelta(years=2, months=6), '2 years, 6 months, 0:00:00'), + (datedelta(days=1, hours=1, minutes=2, seconds=3), '1 day, 1:02:03'), + (datedelta(weeks=1, days=1), '8 days, 0:00:00'), + ) + for (delta, target) in cases: + assert str(delta) == target + + +def test_object_representation(): + cases = ( + (datedelta(years=1), 'datedelta(1, 0, datetime.timedelta(0))'), + (datedelta(years=2, months=6), 'datedelta(2, 6, datetime.timedelta(0))'), + (datedelta(days=1, hours=1, minutes=2, seconds=3), 'datedelta(0, 0, datetime.timedelta(1, 3723))'), + (datedelta(weeks=1, days=1), 'datedelta(0, 0, datetime.timedelta(8))'), + ) + for (delta, target) in cases: + assert repr(delta) == target diff --git a/tests/lib/tests/test_generateParameters.py b/tests/lib/tests/test_generateParameters.py new file mode 100644 index 0000000..a894116 --- /dev/null +++ b/tests/lib/tests/test_generateParameters.py @@ -0,0 +1,37 @@ +import os +import pytest + +from tests.lib.fixtures import generateParameters + + +@pytest.fixture +def basedir(): + return os.path.dirname(__file__) + + +def test_empty_file(basedir): + parameters = ['target', 'phrase'] + result = generateParameters('empty.yml', basedir, [], parameters) + assert result == (','.join(parameters), []) + + +def test_known_parameters(basedir): + targetParameters = [ + 'sourceTime', + 'target', + 'phrase', + 'context', + 'calendar', + 'nlpTarget', + 'case', + ] + parameters = targetParameters + ['unknown'] + result = generateParameters('empty.yml', basedir, [], parameters) + assert result == (','.join(targetParameters), []) + + +def test_unknown_locales(basedir): + parameters = ['target'] + result = generateParameters('simple.yml', basedir, [], parameters, + localeIDs=['xx', 'yy']) + assert result == (','.join(parameters), []) diff --git a/tests/lib/tests/test_nlpTarget.py b/tests/lib/tests/test_nlpTarget.py new file mode 100644 index 0000000..d35c50f --- /dev/null +++ b/tests/lib/tests/test_nlpTarget.py @@ -0,0 +1,72 @@ +from datetime import datetime +import pytest + +from tests.lib.models import nlpTarget, TestCase + + +@pytest.fixture +def nowTargets(): + return [{ + 'phrase': 'now', + 'target': datetime.now(), + }] + +@pytest.fixture +def testCase(calendar): + caseData = { + 'phrases': ['now'], + 'target': datetime.now(), + } + return TestCase(caseData, calendar, datetime.now()) + +otherTestCase = testCase + + +def test_equality(nowTargets, testCase, otherTestCase): + sourcePhrase = 'test now' + target = nlpTarget(nowTargets, testCase, sourcePhrase) + otherTarget = nlpTarget(nowTargets, otherTestCase, sourcePhrase) + assert target == otherTarget + assert target == otherTarget.tupleValue + assert not (target == 5) + + +def test_explicit_startIndex(nowTargets, testCase, otherTestCase): + sourcePhrase = 'test now' + startIndex = 11 + nowTargets[0]['startIndex'] = startIndex + target = nlpTarget(nowTargets, testCase, sourcePhrase) + assert target.tupleValue[0][2] == startIndex + assert target.tupleValue[0][3] == startIndex + len('now') + + +def test_object_representation(nowTargets, testCase): + sourcePhrase = 'test now' + now = nowTargets[0]['target'] + target = nlpTarget(nowTargets, testCase, sourcePhrase) + representation = 'nlpTarget((%r, pdtContext(), 5, 8, \'now\'),)' % now + assert repr(target) == representation + + +def test_raises_error_for_phrase_not_in_sourcePhrase(nowTargets, testCase): + sourcePhrase = 'test' + target = nlpTarget(nowTargets, testCase, sourcePhrase) + with pytest.raises(ValueError) as excinfo: + target.tupleValue + assert excinfo.match(r'\bphrase\b.*\bnot contained\b') + + +def test_raises_error_for_no_testCase(nowTargets): + sourcePhrase = 'test' + target = nlpTarget(nowTargets, sourcePhrase=sourcePhrase) + with pytest.raises(ValueError) as excinfo: + target.tupleValue + assert excinfo.match(r'\btestCase\b') + + +def test_raises_error_for_no_sourcePhrase(nowTargets, testCase): + sourcePhrase = 'test' + target = nlpTarget(nowTargets, testCase) + with pytest.raises(ValueError) as excinfo: + target.tupleValue + assert excinfo.match(r'\bsourcePhrase\b') diff --git a/tests/lib/tests/test_nlpTargetValue.py b/tests/lib/tests/test_nlpTargetValue.py new file mode 100644 index 0000000..8b6f424 --- /dev/null +++ b/tests/lib/tests/test_nlpTargetValue.py @@ -0,0 +1,39 @@ +from datetime import datetime +import pytest + +from tests.lib.models import nlpTargetValue +from parsedatetime.context import pdtContext + + +def test_values(): + phrase = 'now' + target = datetime.now() + context = pdtContext(pdtContext.ACU_NOW) + startIndex = 12 + target_value = nlpTargetValue(phrase, target, context=context, + startIndex=startIndex) + assert target_value.phrase == phrase + assert target_value.target == target + assert target_value.context == context + assert target_value.startIndex == startIndex + + +def test_defaults_to_wildcard_context(): + target_value = nlpTargetValue('now', datetime.now()) + assert target_value.context == pdtContext(pdtContext.ACU_WILDCARD) + + +def test_wrong_phrase_type(): + with pytest.raises(TypeError): + nlpTargetValue(1, datetime.now()) + + +def test_wrong_context_type(): + with pytest.raises(TypeError): + nlpTargetValue('now', datetime.now(), context=1) + + +def test_wrong_startIndex_type(): + with pytest.raises(TypeError): + nlpTargetValue('now', datetime.now(), startIndex='bad') + diff --git a/tests/lib/tests/test_pdtFixture.py b/tests/lib/tests/test_pdtFixture.py new file mode 100644 index 0000000..b789695 --- /dev/null +++ b/tests/lib/tests/test_pdtFixture.py @@ -0,0 +1,66 @@ +import os +import pytest + +import tests.lib.fixtures as fixtures +from tests.lib.fixtures import pdtFixture + + +@pytest.fixture(scope='function', autouse=True) +def patch_generateParameters(mocker): + mocker.patch.object(fixtures, 'generateParameters') + +@pytest.fixture +def basedir(): + return os.path.dirname(__file__) + + +def test_implicit_group_name(basedir): + filename = 'simple.yml' + testGroupNames = ['noon'] + parameters = [] + localeIDs = None + @pdtFixture(filename) + def test_noon(): + pass + fixtures.generateParameters.assert_called_once_with(filename, basedir, + testGroupNames, + parameters, localeIDs) + + +def test_explicit_group_name(basedir): + filename = 'simple.yml' + testGroupNames = ['noon'] + parameters = [] + localeIDs = None + @pdtFixture(filename, explicitTestGroupNames=testGroupNames) + def test_foo(): + pass + fixtures.generateParameters.assert_called_once_with(filename, basedir, + testGroupNames, + parameters, localeIDs) + + +def test_parameters(basedir): + filename = 'simple.yml' + testGroupNames = ['noon'] + parameters = ['a', 'b', 'c'] + localeIDs = None + @pdtFixture(filename) + def test_noon(a, b, c): + pass + fixtures.generateParameters.assert_called_once_with(filename, basedir, + testGroupNames, + parameters, localeIDs) + + +def test_localeIDs(basedir): + filename = 'simple.yml' + testGroupNames = ['noon'] + parameters = [] + localeIDs = ['en_US'] + @pdtFixture(filename, localeIDs=localeIDs) + def test_noon(): + pass + fixtures.generateParameters.assert_called_once_with(filename, basedir, + testGroupNames, + parameters, localeIDs) diff --git a/tests/lib/tests/test_yaml.py b/tests/lib/tests/test_yaml.py new file mode 100644 index 0000000..9ca9ceb --- /dev/null +++ b/tests/lib/tests/test_yaml.py @@ -0,0 +1,40 @@ +import os +import pytest +from yaml import YAMLError + +from parsedatetime.context import pdtContext +from tests.lib.data import loadData +from tests.lib.models import datedelta, dateReplacement + +@pytest.fixture +def basedir(): + return os.path.dirname(__file__) + + +@pytest.fixture +def constructorData(basedir): + return loadData(os.path.join(basedir, 'data/en_US/constructors.yml')) + + +def test_yaml_error(basedir): + with pytest.raises(YAMLError): + loadData(os.path.join(basedir, 'data/en_US/yaml_error.yml')) + + +def test_io_error(basedir): + with pytest.raises(IOError): + loadData(os.path.join(basedir, 'data/en_US/does_not_exist.yml')) + + +def test_datedelta_constructor(constructorData): + constructorData['datedelta'] == datedelta(years=1, days=1) + + +def test_dateReplacement_constructor(constructorData): + constructorData['dateReplacement'] == dateReplacement(month=1, hour=1) + + +def test_pdtContext_constructor(constructorData): + flags = pdtContext.ACU_YEAR | pdtContext.ACU_HOUR | pdtContext.ACU_SEC + constructorData['pdtContext'] == pdtContext(flags) + \ No newline at end of file diff --git a/tests/test_complex_datetimes.py b/tests/test_complex_datetimes.py new file mode 100644 index 0000000..ea41d67 --- /dev/null +++ b/tests/test_complex_datetimes.py @@ -0,0 +1,57 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('complex_datetimes.yml') +def test_year_month_day_and_time(calendar, phrase, sourceTime, target, + context, assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('complex_datetimes.yml') +def test_future_month_day_and_time(calendar, phrase, sourceTime, target, + context, assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('complex_datetimes.yml') +def test_past_month_day_and_time(calendar, phrase, sourceTime, target, + context, assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('complex_datetimes.yml') +def test_date_variations(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('complex_datetimes.yml') +def test_future_date_variations(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('complex_datetimes.yml') +def test_past_date_variations(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('complex_datetimes.yml') +def test_dates_with_weekday(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context diff --git a/tests/test_convertUnitAsWords.py b/tests/test_convertUnitAsWords.py new file mode 100644 index 0000000..4724e77 --- /dev/null +++ b/tests/test_convertUnitAsWords.py @@ -0,0 +1,6 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('numbers_as_words.yml') +def test_numbers_as_words(calendar, phrase, target): + assert calendar._convertUnitAsWords(phrase) == target diff --git a/tests/test_day_start_hour.py b/tests/test_day_start_hour.py new file mode 100644 index 0000000..1259655 --- /dev/null +++ b/tests/test_day_start_hour.py @@ -0,0 +1,21 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('day_start_hour.yml') +def test_default_day_start_hour(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('day_start_hour.yml') +def test_midnight_day_start_hour(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('day_start_hour.yml') +def test_morning_day_start_hour(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('day_start_hour.yml') +def test_evening_day_start_hour(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) diff --git a/tests/test_delta.py b/tests/test_delta.py new file mode 100644 index 0000000..0e129f4 --- /dev/null +++ b/tests/test_delta.py @@ -0,0 +1,22 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('deltas.yml') +def test_past_integer_values(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('deltas.yml') +def test_past_float_values(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('deltas.yml') +def test_simple_multiple_unit_deltas(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('deltas.yml') +def test_abbreviated_multiple_unit_deltas(calendar, phrase, sourceTime, target, + context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) diff --git a/tests/test_errors.py b/tests/test_errors.py new file mode 100644 index 0000000..480e6d8 --- /dev/null +++ b/tests/test_errors.py @@ -0,0 +1,31 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('errors.yml') +def test_out_of_range(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), + context) + + +@pdtFixture('errors.yml') +def test_plain_numbers(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), + context) + + +@pdtFixture('errors.yml') +def test_substring_phrases(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), + context) + + +@pdtFixture('errors.yml') +def test_substring_dates(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), + context) + + +@pdtFixture('errors.yml') +def test_overflow(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), + context) diff --git a/tests/test_inc.py b/tests/test_inc.py new file mode 100644 index 0000000..c1d3884 --- /dev/null +++ b/tests/test_inc.py @@ -0,0 +1,69 @@ +import datetime + + +def test_inc_months(calendar): + s = datetime.datetime(2006, 1, 1, 12, 0, 0) + t = datetime.datetime(2006, 2, 1, 12, 0, 0) + assert calendar.inc(s, month=1).timetuple() == t.timetuple() + + s = datetime.datetime(2006, 12, 1, 12, 0, 0) + t = datetime.datetime(2007, 1, 1, 12, 0, 0) + assert calendar.inc(s, month=1).timetuple() == t.timetuple() + + # leap year, Feb 1 + s = datetime.datetime(2008, 2, 1, 12, 0, 0) + t = datetime.datetime(2008, 3, 1, 12, 0, 0) + assert calendar.inc(s, month=1).timetuple() == t.timetuple() + + # leap year, Feb 29 + s = datetime.datetime(2008, 2, 29, 12, 0, 0) + t = datetime.datetime(2008, 3, 29, 12, 0, 0) + assert calendar.inc(s, month=1).timetuple() == t.timetuple() + + s = datetime.datetime(2006, 1, 1, 12, 0, 0) + t = datetime.datetime(2005, 12, 1, 12, 0, 0) + assert calendar.inc(s, month=-1).timetuple() == t.timetuple() + + # End of month Jan 31 to Feb - Febuary only has 28 days + s = datetime.datetime(2006, 1, 31, 12, 0, 0) + t = datetime.datetime(2006, 2, 28, 12, 0, 0) + assert calendar.inc(s, month=1).timetuple() == t.timetuple() + + # walk thru months and make sure month increment doesn't set the day + # to be past the last day of the new month + # think Jan transition to Feb - 31 days to 28 days + for m in range(1, 11): + d = calendar.ptc.daysInMonth(m, 2006) + s = datetime.datetime(2006, m, d, 12, 0, 0) + + if d > calendar.ptc.daysInMonth(m + 1, 2006): + d = calendar.ptc.daysInMonth(m + 1, 2006) + + t = datetime.datetime(2006, m + 1, d, 12, 0, 0) + + assert calendar.inc(s, month=1).timetuple() == t.timetuple() + +def test_inc_years(calendar): + s = datetime.datetime(2006, 1, 1, 12, 0, 0) + t = datetime.datetime(2007, 1, 1, 12, 0, 0) + assert calendar.inc(s, year=1).timetuple() == t.timetuple() + + s = datetime.datetime(2006, 1, 1, 12, 0, 0) + t = datetime.datetime(2008, 1, 1, 12, 0, 0) + assert calendar.inc(s, year=2).timetuple() == t.timetuple() + + s = datetime.datetime(2006, 12, 31, 12, 0, 0) + t = datetime.datetime(2007, 12, 31, 12, 0, 0) + assert calendar.inc(s, year=1).timetuple() == t.timetuple() + + s = datetime.datetime(2006, 12, 31, 12, 0, 0) + t = datetime.datetime(2005, 12, 31, 12, 0, 0) + assert calendar.inc(s, year=-1).timetuple() == t.timetuple() + + s = datetime.datetime(2008, 3, 1, 12, 0, 0) + t = datetime.datetime(2009, 3, 1, 12, 0, 0) + assert calendar.inc(s, year=1).timetuple() == t.timetuple() + + s = datetime.datetime(2008, 3, 1, 12, 0, 0) + t = datetime.datetime(2007, 3, 1, 12, 0, 0) + assert calendar.inc(s, year=-1).timetuple() == t.timetuple() \ No newline at end of file diff --git a/tests/test_names.py b/tests/test_names.py new file mode 100644 index 0000000..5df1a15 --- /dev/null +++ b/tests/test_names.py @@ -0,0 +1,26 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('names.yml') +def test_week_day_names(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('names.yml') +def test_invalid_week_day_names(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), + context) + + +@pdtFixture('names.yml') +def test_month_names(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('names.yml') +def test_invalid_month_names(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), + context) diff --git a/tests/test_nlp.py b/tests/test_nlp.py new file mode 100644 index 0000000..669fc1d --- /dev/null +++ b/tests/test_nlp.py @@ -0,0 +1,53 @@ +import pytest + +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('nlp.yml') +def test_long_phrases(calendar, phrase, sourceTime, nlpTarget): + assert calendar.nlp(phrase, sourceTime) == nlpTarget + + +@pdtFixture('nlp.yml') +def test_long_phrases_with_quotes(calendar, phrase, sourceTime, nlpTarget): + assert calendar.nlp(phrase, sourceTime) == nlpTarget + + +@pdtFixture('nlp.yml') +def test_prefixes(calendar, phrase, sourceTime, nlpTarget): + assert calendar.nlp(phrase, sourceTime) == nlpTarget + + +@pdtFixture('nlp.yml') +def test_invalid_phrases(calendar, phrase, sourceTime, nlpTarget): + assert calendar.nlp(phrase, sourceTime) == nlpTarget + + +@pdtFixture('simple_datetimes.yml') +def test_times(calendar, phrase, sourceTime, nlpTarget): + assert calendar.nlp(phrase, sourceTime) == nlpTarget + + +@pdtFixture('simple_datetimes.yml') +def test_invalid_times(calendar, phrase, sourceTime, nlpTarget): + assert calendar.nlp(phrase, sourceTime) == nlpTarget + + +@pytest.mark.parametrize('prefix,suffix', (('"', '"'), ("'", "'"), ('(', ')'))) +@pdtFixture('simple_datetimes.yml', ['times', 'invalid_times', 'dates', + 'invalid_dates']) +def test_simple_datetimes_wrapped(calendar, phrase, sourceTime, nlpTarget, prefix, + suffix): + sourcePhrase = u'%s%s%s' % (prefix, phrase, suffix) + nlpTarget.sourcePhrase = sourcePhrase + assert calendar.nlp(sourcePhrase, sourceTime) == nlpTarget + + +@pdtFixture('deltas.yml', ['past_integer_values', 'past_float_values'], + # FIXME: Simple tests in German locale fail + localeIDs=['en_US']) +def test_deltas(calendar, phrase, sourceTime, nlpTarget): + # FIXME: these tests fail + if phrase in ('1855336.424 minutes ago',): + return + assert calendar.nlp(phrase, sourceTime) == nlpTarget diff --git a/tests/test_phrases.py b/tests/test_phrases.py new file mode 100644 index 0000000..ec39163 --- /dev/null +++ b/tests/test_phrases.py @@ -0,0 +1,24 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('phrases.yml') +def test_phrases(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('phrases.yml') +def test_weekday_phrases(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('phrases.yml') +def test_end_of_phrases(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('phrases.yml') +def test_last_phrases(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) diff --git a/tests/test_ranges.py b/tests/test_ranges.py new file mode 100644 index 0000000..b7d315e --- /dev/null +++ b/tests/test_ranges.py @@ -0,0 +1,18 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('ranges.yml') +def test_times(calendar, phrase, sourceTime, target, context): + (startTarget, endTarget) = target + assert (calendar.evalRanges(phrase, sourceTime) == + (startTarget.timetuple(), endTarget.timetuple(), context)) + + +@pdtFixture('ranges.yml') +def test_dates(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + (startTarget, endTarget) = target + (start, end, resultContext) = calendar.evalRanges(phrase, sourceTime) + assertLazyStructTimes(start, startTarget.timetuple()) + assertLazyStructTimes(end, endTarget.timetuple()) + assert resultContext == context diff --git a/tests/test_simple_datetimes.py b/tests/test_simple_datetimes.py new file mode 100644 index 0000000..0b79678 --- /dev/null +++ b/tests/test_simple_datetimes.py @@ -0,0 +1,121 @@ +import datetime +import string + +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('simple_datetimes.yml') +def test_times(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_datetimes.yml') +def test_invalid_times(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), context) + + +@pdtFixture('simple_datetimes.yml') +def test_dates(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('simple_datetimes.yml') +def test_invalid_dates(calendar, phrase, sourceTime, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), context) + + +@pdtFixture('simple_datetimes.yml') +def test_day_suffixes(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('simple_datetimes.yml') +def test_special_times(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_datetimes.yml') +def test_midnight(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_datetimes.yml') +def test_noon(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_datetimes.yml') +def test_leap_days(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('simple_datetimes.yml') +def test_year_parse_style_1(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('simple_datetimes.yml') +def test_year_parse_style_0(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +def test_days_in_month(calendar): + dNormal = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + dLeap = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + + for i in range(1, 12): + assert calendar.ptc.daysInMonth(i, 1999) == dNormal[i - 1] + assert calendar.ptc.daysInMonth(i, 2000) == dLeap[i - 1] + assert calendar.ptc.daysInMonth(i, 2001) == dNormal[i - 1] + assert calendar.ptc.daysInMonth(i, 2002) == dNormal[i - 1] + assert calendar.ptc.daysInMonth(i, 2003) == dNormal[i - 1] + assert calendar.ptc.daysInMonth(i, 2004) == dLeap[i - 1] + assert calendar.ptc.daysInMonth(i, 2005) == dNormal[i - 1] + + +def test_word_boundaries(calendar): + start = target = datetime.datetime.now().timetuple() + loc = calendar.ptc.locale + keywords = [] + + def flattenWeekdays(wds): + return sum([wd.split('|') for wd in wds], []) + + # Test all known keywords for the locale + keywords.extend(loc.meridian) + keywords.extend(flattenWeekdays(loc.Weekdays)) + keywords.extend(flattenWeekdays(loc.shortWeekdays)) + keywords.extend(loc.Months) + keywords.extend(loc.shortMonths) + keywords.extend(loc.numbers.keys()) + keywords.extend(loc.Modifiers.keys()) + keywords.extend(loc.dayOffsets.keys()) + keywords.extend(loc.re_sources.keys()) + keywords.extend(loc.small.keys()) + keywords.extend(loc.magnitude.keys()) + + for units in loc.units.values(): + keywords.extend(units) + + # Finally, test all lowercase letters to be particularly thorough - it + # would be very difficult to track down bugs due to single letters. + keywords.extend(list(string.ascii_lowercase)) + + for keyword in keywords: + phrase = '1 %sfoo' % keyword + assert calendar.parse(phrase, start) == (target, 0) diff --git a/tests/test_simple_offsets.py b/tests/test_simple_offsets.py new file mode 100644 index 0000000..2fcf328 --- /dev/null +++ b/tests/test_simple_offsets.py @@ -0,0 +1,65 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('simple_offsets.yml') +def test_now(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_offset_from_day_of_week(calendar, phrase, sourceTime, target, + context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_offset_from_day_of_week_matching_source_time(calendar, phrase, + sourceTime, target, + context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_minutes_from_now(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_minutes_before_now(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_week_from_now(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_week_before_now(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_next_month(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_next_weekday(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_next_weekday_matching_source_time(calendar, phrase, sourceTime, + target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_next_weekday_with_time(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets.yml') +def test_specials(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) diff --git a/tests/test_simple_offsets_hours.py b/tests/test_simple_offsets_hours.py new file mode 100644 index 0000000..9515784 --- /dev/null +++ b/tests/test_simple_offsets_hours.py @@ -0,0 +1,27 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('simple_offsets_hours.yml') +def test_hours_from_now(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets_hours.yml') +def test_invalid_hours_from_now(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (sourceTime.timetuple(), + context) + + +@pdtFixture('simple_offsets_hours.yml') +def test_hours_before_now(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets_hours.yml') +def test_hours_from_noon(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('simple_offsets_hours.yml') +def test_hours_before_noon(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) diff --git a/tests/test_start_time_from_source_time.py b/tests/test_start_time_from_source_time.py new file mode 100644 index 0000000..775b531 --- /dev/null +++ b/tests/test_start_time_from_source_time.py @@ -0,0 +1,17 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('start_time_from_source_time.yml') +def test_end_of_phrases_enabled(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context + + +@pdtFixture('start_time_from_source_time.yml') +def test_end_of_phrases_disabled(calendar, phrase, sourceTime, target, context, + assertLazyStructTimes): + result = calendar.parse(phrase, sourceTime) + assertLazyStructTimes(result[0], target.timetuple()) + assert result[1] == context diff --git a/tests/test_units.py b/tests/test_units.py new file mode 100644 index 0000000..bb993c3 --- /dev/null +++ b/tests/test_units.py @@ -0,0 +1,31 @@ +from tests.lib.fixtures import pdtFixture + + +@pdtFixture('units.yml') +def test_minutes(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('units.yml') +def test_hours(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('units.yml') +def test_days(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('units.yml') +def test_weeks(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('units.yml') +def test_months(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) + + +@pdtFixture('units.yml') +def test_years(calendar, phrase, sourceTime, target, context): + assert calendar.parse(phrase, sourceTime) == (target.timetuple(), context) diff --git a/tests/utils.py b/tests/utils.py deleted file mode 100644 index 92435d7..0000000 --- a/tests/utils.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Internal helper functions for unit tests of parsedatetime -""" -from __future__ import unicode_literals - - -def assertEqualWithComparator(comparator): - """ - Fail a little less cryptically that unittest.assertTrue when comparing a - result against a target value. Shows the result and the target in the - failure message. - """ - - def decoratedComparator(self, result, check, errMsg=None, **kwargs): - errMsg = errMsg or 'Result does not match target value' - equal = comparator(self, result, check, **kwargs) - failureMessage = ('%s\n\n\t' - 'Result:\n\t%s\n\n\tExpected:\n\t%s') - - if not equal: - self.fail(failureMessage % (errMsg, result, check)) - - return decoratedComparator - - -def compareResultByTimeTuplesAndFlags(result, check, dateOnly=False): - """ - Ensures that flags are an exact match and time tuples a close match when - given data in the format ((timetuple), flag) - """ - return (_compareTimeTuples(result[0], check[0], dateOnly) and - _compareFlags(result[1], check[1])) - - -def compareResultByFlags(result, check, dateOnly=False): - """ - Ensures that flags are an exact match when given data in the format - ((timetuple), flag) - """ - return _compareFlags(result[1], check[1]) - - -def compareResultByTimeTupleRangesAndFlags(result, check, dateOnly=False): - """ - Ensures that flags are an exact match and time tuples a close match when - given data in the format ((timetuple), (timetuple), flag) - """ - return (_compareTimeTuples(result[0], check[0], dateOnly) and - _compareTimeTuples(result[1], check[1], dateOnly) and - _compareFlags(result[2], check[2])) - - -def _compareTimeTuples(target, value, dateOnly=False): - """ - Ignores minutes and seconds as running the test could cross a minute - boundary. Technically the year, month, day, hour, minute, and second could - all change if the test is run on New Year's Eve, but we won't worry about - less than per-hour granularity. - """ - t_yr, t_mth, t_dy, t_hr, t_min, _, _, _, _ = target - v_yr, v_mth, v_dy, v_hr, v_min, _, _, _, _ = value - - if dateOnly: - return ((t_yr == v_yr) and (t_mth == v_mth) and (t_dy == v_dy)) - else: - return ((t_yr == v_yr) and (t_mth == v_mth) and (t_dy == v_dy) and - (t_hr == v_hr) and (t_min == v_min)) - - -def _compareFlags(result, check): - return (result == check)