# -*- coding: utf-8 -*-

# Copyright (c) 2025, Brandon Nielsen
# All rights reserved.
#
# This software may be modified and distributed under the terms
# of the BSD license.  See the LICENSE file for details.

import unittest

import aniso8601
from aniso8601.builders import (
    DatetimeTuple,
    DateTuple,
    DurationTuple,
    IntervalTuple,
    TimeTuple,
    TimezoneTuple,
)
from aniso8601.exceptions import ISOFormatError
from aniso8601.interval import (
    _get_interval_component_resolution,
    _get_interval_resolution,
    _parse_interval,
    _parse_interval_end,
    get_interval_resolution,
    get_repeating_interval_resolution,
    parse_interval,
    parse_repeating_interval,
)
from aniso8601.resolution import IntervalResolution
from aniso8601.tests.compat import mock


class TestIntervalParser_UtilityFunctions(unittest.TestCase):
    def test_get_interval_resolution(self):
        self.assertEqual(
            _get_interval_resolution(
                IntervalTuple(
                    start=DateTuple(
                        YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                    ),
                    end=DatetimeTuple(
                        DateTuple(
                            YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                        ),
                        TimeTuple(hh="04", mm="05", ss="06", tz=None),
                    ),
                    duration=None,
                )
            ),
            IntervalResolution.Seconds,
        )
        self.assertEqual(
            _get_interval_resolution(
                IntervalTuple(
                    start=DatetimeTuple(
                        DateTuple(
                            YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                        ),
                        TimeTuple(hh="04", mm="05", ss="06", tz=None),
                    ),
                    end=DateTuple(
                        YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                    ),
                    duration=None,
                )
            ),
            IntervalResolution.Seconds,
        )

        self.assertEqual(
            _get_interval_resolution(
                IntervalTuple(
                    start=DateTuple(
                        YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                    ),
                    end=None,
                    duration=DurationTuple(
                        PnY="1", PnM="2", PnW=None, PnD="3", TnH="4", TnM="5", TnS="6"
                    ),
                )
            ),
            IntervalResolution.Seconds,
        )
        self.assertEqual(
            _get_interval_resolution(
                IntervalTuple(
                    start=DatetimeTuple(
                        DateTuple(
                            YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                        ),
                        TimeTuple(hh="04", mm="05", ss="06", tz=None),
                    ),
                    end=None,
                    duration=DurationTuple(
                        PnY="1",
                        PnM="2",
                        PnW=None,
                        PnD="3",
                        TnH=None,
                        TnM=None,
                        TnS=None,
                    ),
                )
            ),
            IntervalResolution.Seconds,
        )

        self.assertEqual(
            _get_interval_resolution(
                IntervalTuple(
                    start=None,
                    end=DateTuple(
                        YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                    ),
                    duration=DurationTuple(
                        PnY="1", PnM="2", PnW=None, PnD="3", TnH="4", TnM="5", TnS="6"
                    ),
                )
            ),
            IntervalResolution.Seconds,
        )
        self.assertEqual(
            _get_interval_resolution(
                IntervalTuple(
                    start=None,
                    end=DatetimeTuple(
                        DateTuple(
                            YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                        ),
                        TimeTuple(hh="04", mm="05", ss="06", tz=None),
                    ),
                    duration=DurationTuple(
                        PnY="1",
                        PnM="2",
                        PnW=None,
                        PnD="3",
                        TnH=None,
                        TnM=None,
                        TnS=None,
                    ),
                )
            ),
            IntervalResolution.Seconds,
        )

    def test_get_interval_component_resolution(self):
        self.assertEqual(
            _get_interval_component_resolution(
                DateTuple(YYYY="2001", MM=None, DD=None, Www=None, D=None, DDD="123")
            ),
            IntervalResolution.Ordinal,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DateTuple(YYYY="2001", MM=None, DD=None, Www="12", D="3", DDD=None)
            ),
            IntervalResolution.Weekday,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DateTuple(YYYY="2001", MM=None, DD=None, Www="12", D=None, DDD=None)
            ),
            IntervalResolution.Week,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DateTuple(YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None)
            ),
            IntervalResolution.Day,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DateTuple(YYYY="2001", MM="02", DD=None, Www=None, D=None, DDD=None)
            ),
            IntervalResolution.Month,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DateTuple(YYYY="2001", MM=None, DD=None, Www=None, D=None, DDD=None)
            ),
            IntervalResolution.Year,
        )

        self.assertEqual(
            _get_interval_component_resolution(
                DatetimeTuple(
                    DateTuple(
                        YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                    ),
                    TimeTuple(hh="04", mm="05", ss="06", tz=None),
                )
            ),
            IntervalResolution.Seconds,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DatetimeTuple(
                    DateTuple(
                        YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                    ),
                    TimeTuple(hh="04", mm="05", ss=None, tz=None),
                )
            ),
            IntervalResolution.Minutes,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DatetimeTuple(
                    DateTuple(
                        YYYY="2001", MM="02", DD="03", Www=None, D=None, DDD=None
                    ),
                    TimeTuple(hh="04", mm=None, ss=None, tz=None),
                )
            ),
            IntervalResolution.Hours,
        )

        self.assertEqual(
            _get_interval_component_resolution(
                DurationTuple(
                    PnY="1", PnM="2", PnW=None, PnD="3", TnH="4", TnM="5", TnS="6"
                )
            ),
            IntervalResolution.Seconds,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DurationTuple(
                    PnY="1", PnM="2", PnW=None, PnD="3", TnH="4", TnM="5", TnS=None
                )
            ),
            IntervalResolution.Minutes,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DurationTuple(
                    PnY="1", PnM="2", PnW=None, PnD="3", TnH="4", TnM=None, TnS=None
                )
            ),
            IntervalResolution.Hours,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DurationTuple(
                    PnY="1", PnM="2", PnW=None, PnD="3", TnH=None, TnM=None, TnS=None
                )
            ),
            IntervalResolution.Day,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DurationTuple(
                    PnY="1", PnM="2", PnW=None, PnD=None, TnH=None, TnM=None, TnS=None
                )
            ),
            IntervalResolution.Month,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DurationTuple(
                    PnY="1", PnM=None, PnW=None, PnD=None, TnH=None, TnM=None, TnS=None
                )
            ),
            IntervalResolution.Year,
        )
        self.assertEqual(
            _get_interval_component_resolution(
                DurationTuple(
                    PnY=None, PnM=None, PnW="3", PnD=None, TnH=None, TnM=None, TnS=None
                )
            ),
            IntervalResolution.Week,
        )


class TestIntervalParserFunctions(unittest.TestCase):
    def test_get_interval_resolution_date(self):
        self.assertEqual(get_interval_resolution("P1.5Y/2018"), IntervalResolution.Year)
        self.assertEqual(
            get_interval_resolution("P1.5Y/2018-03"), IntervalResolution.Month
        )
        self.assertEqual(
            get_interval_resolution("P1.5Y/2018-03-06"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("P1.5Y/2018W01"), IntervalResolution.Week
        )
        self.assertEqual(
            get_interval_resolution("P1.5Y/2018-306"), IntervalResolution.Ordinal
        )
        self.assertEqual(
            get_interval_resolution("P1.5Y/2018W012"), IntervalResolution.Weekday
        )

        self.assertEqual(get_interval_resolution("2018/P1.5Y"), IntervalResolution.Year)
        self.assertEqual(
            get_interval_resolution("2018-03/P1.5Y"), IntervalResolution.Month
        )
        self.assertEqual(
            get_interval_resolution("2018-03-06/P1.5Y"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("2018W01/P1.5Y"), IntervalResolution.Week
        )
        self.assertEqual(
            get_interval_resolution("2018-306/P1.5Y"), IntervalResolution.Ordinal
        )
        self.assertEqual(
            get_interval_resolution("2018W012/P1.5Y"), IntervalResolution.Weekday
        )

    def test_get_interval_resolution_time(self):
        self.assertEqual(
            get_interval_resolution("P1M/1981-04-05T01"), IntervalResolution.Hours
        )
        self.assertEqual(
            get_interval_resolution("P1M/1981-04-05T01:01"), IntervalResolution.Minutes
        )
        self.assertEqual(
            get_interval_resolution("P1M/1981-04-05T01:01:00"),
            IntervalResolution.Seconds,
        )

        self.assertEqual(
            get_interval_resolution("1981-04-05T01/P1M"), IntervalResolution.Hours
        )
        self.assertEqual(
            get_interval_resolution("1981-04-05T01:01/P1M"), IntervalResolution.Minutes
        )
        self.assertEqual(
            get_interval_resolution("1981-04-05T01:01:00/P1M"),
            IntervalResolution.Seconds,
        )

    def test_get_interval_resolution_duration(self):
        self.assertEqual(
            get_interval_resolution("2014-11-12/P1Y2M3D"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("2014-11-12/P1Y2M"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("2014-11-12/P1Y"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("2014-11-12/P1W"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("2014-11-12/P1Y2M3DT4H"), IntervalResolution.Hours
        )
        self.assertEqual(
            get_interval_resolution("2014-11-12/P1Y2M3DT4H54M"),
            IntervalResolution.Minutes,
        )
        self.assertEqual(
            get_interval_resolution("2014-11-12/P1Y2M3DT4H54M6S"),
            IntervalResolution.Seconds,
        )

        self.assertEqual(
            get_interval_resolution("P1Y2M3D/2014-11-12"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("P1Y2M/2014-11-12"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("P1Y/2014-11-12"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("P1W/2014-11-12"), IntervalResolution.Day
        )
        self.assertEqual(
            get_interval_resolution("P1Y2M3DT4H/2014-11-12"), IntervalResolution.Hours
        )
        self.assertEqual(
            get_interval_resolution("P1Y2M3DT4H54M/2014-11-12"),
            IntervalResolution.Minutes,
        )
        self.assertEqual(
            get_interval_resolution("P1Y2M3DT4H54M6S/2014-11-12"),
            IntervalResolution.Seconds,
        )

    def test_parse_interval(self):
        testtuples = (
            (
                "P1M/1981-04-05T01:01:00",
                {
                    "end": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    "duration": DurationTuple(None, "1", None, None, None, None, None),
                },
            ),
            (
                "P1M/1981-04-05",
                {
                    "end": DateTuple("1981", "04", "05", None, None, None),
                    "duration": DurationTuple(None, "1", None, None, None, None, None),
                },
            ),
            (
                "P1,5Y/2018-03-06",
                {
                    "end": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        "1.5", None, None, None, None, None, None
                    ),
                },
            ),
            (
                "P1.5Y/2018-03-06",
                {
                    "end": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        "1.5", None, None, None, None, None, None
                    ),
                },
            ),
            (
                "PT1H/2014-11-12",
                {
                    "end": DateTuple("2014", "11", "12", None, None, None),
                    "duration": DurationTuple(None, None, None, None, "1", None, None),
                },
            ),
            (
                "PT4H54M6.5S/2014-11-12",
                {
                    "end": DateTuple("2014", "11", "12", None, None, None),
                    "duration": DurationTuple(None, None, None, None, "4", "54", "6.5"),
                },
            ),
            (
                "PT10H/2050-03-01T13:00:00Z",
                {
                    "end": DatetimeTuple(
                        DateTuple("2050", "03", "01", None, None, None),
                        TimeTuple(
                            "13",
                            "00",
                            "00",
                            TimezoneTuple(False, True, None, None, "Z"),
                        ),
                    ),
                    "duration": DurationTuple(None, None, None, None, "10", None, None),
                },
            ),
            # Make sure we truncate, not round
            # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
            (
                "PT0.0000001S/2018-03-06",
                {
                    "end": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, None, None, None, None, None, "0.0000001"
                    ),
                },
            ),
            (
                "PT2.0000048S/2018-03-06",
                {
                    "end": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, None, None, None, None, None, "2.0000048"
                    ),
                },
            ),
            (
                "1981-04-05T01:01:00/P1M1DT1M",
                {
                    "start": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    "duration": DurationTuple(None, "1", None, "1", None, "1", None),
                },
            ),
            (
                "1981-04-05/P1M1D",
                {
                    "start": DateTuple("1981", "04", "05", None, None, None),
                    "duration": DurationTuple(None, "1", None, "1", None, None, None),
                },
            ),
            (
                "2018-03-06/P2,5M",
                {
                    "start": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, "2.5", None, None, None, None, None
                    ),
                },
            ),
            (
                "2018-03-06/P2.5M",
                {
                    "start": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, "2.5", None, None, None, None, None
                    ),
                },
            ),
            (
                "2014-11-12/PT1H",
                {
                    "start": DateTuple("2014", "11", "12", None, None, None),
                    "duration": DurationTuple(None, None, None, None, "1", None, None),
                },
            ),
            (
                "2014-11-12/PT4H54M6.5S",
                {
                    "start": DateTuple("2014", "11", "12", None, None, None),
                    "duration": DurationTuple(None, None, None, None, "4", "54", "6.5"),
                },
            ),
            (
                "2050-03-01T13:00:00Z/PT10H",
                {
                    "start": DatetimeTuple(
                        DateTuple("2050", "03", "01", None, None, None),
                        TimeTuple(
                            "13",
                            "00",
                            "00",
                            TimezoneTuple(False, True, None, None, "Z"),
                        ),
                    ),
                    "duration": DurationTuple(None, None, None, None, "10", None, None),
                },
            ),
            # Make sure we truncate, not round
            # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
            (
                "2018-03-06/PT0.0000001S",
                {
                    "start": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, None, None, None, None, None, "0.0000001"
                    ),
                },
            ),
            (
                "2018-03-06/PT2.0000048S",
                {
                    "start": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, None, None, None, None, None, "2.0000048"
                    ),
                },
            ),
            (
                "1980-03-05T01:01:00/1981-04-05T01:01:00",
                {
                    "start": DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    "end": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                },
            ),
            (
                "1980-03-05T01:01:00/1981-04-05",
                {
                    "start": DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    "end": DateTuple("1981", "04", "05", None, None, None),
                },
            ),
            (
                "1980-03-05/1981-04-05T01:01:00",
                {
                    "start": DateTuple("1980", "03", "05", None, None, None),
                    "end": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                },
            ),
            (
                "1980-03-05/1981-04-05",
                {
                    "start": DateTuple("1980", "03", "05", None, None, None),
                    "end": DateTuple("1981", "04", "05", None, None, None),
                },
            ),
            (
                "1981-04-05/1980-03-05",
                {
                    "start": DateTuple("1981", "04", "05", None, None, None),
                    "end": DateTuple("1980", "03", "05", None, None, None),
                },
            ),
            (
                "2050-03-01T13:00:00Z/2050-05-11T15:30:00Z",
                {
                    "start": DatetimeTuple(
                        DateTuple("2050", "03", "01", None, None, None),
                        TimeTuple(
                            "13",
                            "00",
                            "00",
                            TimezoneTuple(False, True, None, None, "Z"),
                        ),
                    ),
                    "end": DatetimeTuple(
                        DateTuple("2050", "05", "11", None, None, None),
                        TimeTuple(
                            "15",
                            "30",
                            "00",
                            TimezoneTuple(False, True, None, None, "Z"),
                        ),
                    ),
                },
            ),
            # Test concise interval
            (
                "2020-01-01/02",
                {
                    "start": DateTuple("2020", "01", "01", None, None, None),
                    "end": DateTuple(None, None, "02", None, None, None),
                },
            ),
            (
                "2008-02-15/03-14",
                {
                    "start": DateTuple("2008", "02", "15", None, None, None),
                    "end": DateTuple(None, "03", "14", None, None, None),
                },
            ),
            (
                "2007-12-14T13:30/15:30",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "12", "14", None, None, None),
                        TimeTuple("13", "30", None, None),
                    ),
                    "end": TimeTuple("15", "30", None, None),
                },
            ),
            (
                "2007-11-13T09:00/15T17:00",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "11", "13", None, None, None),
                        TimeTuple("09", "00", None, None),
                    ),
                    "end": DatetimeTuple(
                        DateTuple(None, None, "15", None, None, None),
                        TimeTuple("17", "00", None, None),
                    ),
                },
            ),
            (
                "2007-11-13T00:00/16T00:00",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "11", "13", None, None, None),
                        TimeTuple("00", "00", None, None),
                    ),
                    "end": DatetimeTuple(
                        DateTuple(None, None, "16", None, None, None),
                        TimeTuple("00", "00", None, None),
                    ),
                },
            ),
            (
                "2007-11-13T09:00Z/15T17:00",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "11", "13", None, None, None),
                        TimeTuple(
                            "09",
                            "00",
                            None,
                            TimezoneTuple(False, True, None, None, "Z"),
                        ),
                    ),
                    "end": DatetimeTuple(
                        DateTuple(None, None, "15", None, None, None),
                        TimeTuple("17", "00", None, None),
                    ),
                },
            ),
            (
                "2007-11-13T00:00/12:34.567",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "11", "13", None, None, None),
                        TimeTuple("00", "00", None, None),
                    ),
                    "end": TimeTuple("12", "34.567", None, None),
                },
            ),
            # Make sure we truncate, not round
            # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
            (
                "1980-03-05T01:01:00.0000001/" "1981-04-05T14:43:59.9999997",
                {
                    "start": DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00.0000001", None),
                    ),
                    "end": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("14", "43", "59.9999997", None),
                    ),
                },
            ),
        )

        for testtuple in testtuples:
            with mock.patch.object(
                aniso8601.interval.PythonTimeBuilder, "build_interval"
            ) as mockBuildInterval:
                mockBuildInterval.return_value = testtuple[1]

                result = parse_interval(testtuple[0])

                self.assertEqual(result, testtuple[1])
                mockBuildInterval.assert_called_once_with(**testtuple[1])

        # Test different separators
        with mock.patch.object(
            aniso8601.interval.PythonTimeBuilder, "build_interval"
        ) as mockBuildInterval:
            expectedargs = {
                "start": DatetimeTuple(
                    DateTuple("1980", "03", "05", None, None, None),
                    TimeTuple("01", "01", "00", None),
                ),
                "end": DatetimeTuple(
                    DateTuple("1981", "04", "05", None, None, None),
                    TimeTuple("01", "01", "00", None),
                ),
            }

            mockBuildInterval.return_value = expectedargs

            result = parse_interval(
                "1980-03-05T01:01:00--1981-04-05T01:01:00", intervaldelimiter="--"
            )

            self.assertEqual(result, expectedargs)
            mockBuildInterval.assert_called_once_with(**expectedargs)

        with mock.patch.object(
            aniso8601.interval.PythonTimeBuilder, "build_interval"
        ) as mockBuildInterval:
            expectedargs = {
                "start": DatetimeTuple(
                    DateTuple("1980", "03", "05", None, None, None),
                    TimeTuple("01", "01", "00", None),
                ),
                "end": DatetimeTuple(
                    DateTuple("1981", "04", "05", None, None, None),
                    TimeTuple("01", "01", "00", None),
                ),
            }

            mockBuildInterval.return_value = expectedargs

            result = parse_interval(
                "1980-03-05 01:01:00/1981-04-05 01:01:00", datetimedelimiter=" "
            )

            self.assertEqual(result, expectedargs)
            mockBuildInterval.assert_called_once_with(**expectedargs)

    def test_parse_interval_mockbuilder(self):
        mockBuilder = mock.Mock()

        expectedargs = {
            "end": DatetimeTuple(
                DateTuple("1981", "04", "05", None, None, None),
                TimeTuple("01", "01", "00", None),
            ),
            "duration": DurationTuple(None, "1", None, None, None, None, None),
        }

        mockBuilder.build_interval.return_value = expectedargs

        result = parse_interval("P1M/1981-04-05T01:01:00", builder=mockBuilder)

        self.assertEqual(result, expectedargs)
        mockBuilder.build_interval.assert_called_once_with(**expectedargs)

        mockBuilder = mock.Mock()

        expectedargs = {
            "start": DateTuple("2014", "11", "12", None, None, None),
            "duration": DurationTuple(None, None, None, None, "1", None, None),
        }

        mockBuilder.build_interval.return_value = expectedargs

        result = parse_interval("2014-11-12/PT1H", builder=mockBuilder)

        self.assertEqual(result, expectedargs)
        mockBuilder.build_interval.assert_called_once_with(**expectedargs)

        mockBuilder = mock.Mock()

        expectedargs = {
            "start": DatetimeTuple(
                DateTuple("1980", "03", "05", None, None, None),
                TimeTuple("01", "01", "00", None),
            ),
            "end": DatetimeTuple(
                DateTuple("1981", "04", "05", None, None, None),
                TimeTuple("01", "01", "00", None),
            ),
        }

        mockBuilder.build_interval.return_value = expectedargs

        result = parse_interval(
            "1980-03-05T01:01:00/1981-04-05T01:01:00", builder=mockBuilder
        )

        self.assertEqual(result, expectedargs)
        mockBuilder.build_interval.assert_called_once_with(**expectedargs)

    def test_parse_interval_badtype(self):
        testtuples = (None, 1, False, 1.234)

        for testtuple in testtuples:
            with self.assertRaises(ValueError):
                parse_interval(testtuple, builder=None)

    def test_parse_interval_baddelimiter(self):
        testtuples = (
            "1980-03-05T01:01:00,1981-04-05T01:01:00",
            "P1M 1981-04-05T01:01:00",
        )

        for testtuple in testtuples:
            with self.assertRaises(ISOFormatError):
                parse_interval(testtuple, builder=None)

    def test_parse_interval_badstr(self):
        testtuples = ("/", "0/0/0", "20.50230/0", "5/%", "1/21", "bad", "")

        for testtuple in testtuples:
            with self.assertRaises(ISOFormatError):
                parse_interval(testtuple, builder=None)

    def test_parse_interval_repeating(self):
        # Parse interval can't parse repeating intervals
        with self.assertRaises(ISOFormatError):
            parse_interval("R3/1981-04-05/P1D")

        with self.assertRaises(ISOFormatError):
            parse_interval("R3/1981-04-05/P0003-06-04T12:30:05.5")

        with self.assertRaises(ISOFormatError):
            parse_interval("R/PT1H2M/1980-03-05T01:01:00")

    def test_parse_interval_suffixgarbage(self):
        # Don't allow garbage after the duration
        # https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
        with self.assertRaises(ISOFormatError):
            parse_interval("2001/P1Dasdf", builder=None)

        with self.assertRaises(ISOFormatError):
            parse_interval("P1Dasdf/2001", builder=None)

        with self.assertRaises(ISOFormatError):
            parse_interval("2001/P0003-06-04T12:30:05.5asdfasdf", builder=None)

        with self.assertRaises(ISOFormatError):
            parse_interval("P0003-06-04T12:30:05.5asdfasdf/2001", builder=None)

    def test_parse_interval_internal(self):
        # Test the internal _parse_interval function
        testtuples = (
            (
                "P1M/1981-04-05T01:01:00",
                {
                    "end": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    "duration": DurationTuple(None, "1", None, None, None, None, None),
                },
            ),
            (
                "P1M/1981-04-05",
                {
                    "end": DateTuple("1981", "04", "05", None, None, None),
                    "duration": DurationTuple(None, "1", None, None, None, None, None),
                },
            ),
            (
                "P1,5Y/2018-03-06",
                {
                    "end": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        "1.5", None, None, None, None, None, None
                    ),
                },
            ),
            (
                "P1.5Y/2018-03-06",
                {
                    "end": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        "1.5", None, None, None, None, None, None
                    ),
                },
            ),
            (
                "PT1H/2014-11-12",
                {
                    "end": DateTuple("2014", "11", "12", None, None, None),
                    "duration": DurationTuple(None, None, None, None, "1", None, None),
                },
            ),
            (
                "PT4H54M6.5S/2014-11-12",
                {
                    "end": DateTuple("2014", "11", "12", None, None, None),
                    "duration": DurationTuple(None, None, None, None, "4", "54", "6.5"),
                },
            ),
            # Make sure we truncate, not round
            # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
            (
                "PT0.0000001S/2018-03-06",
                {
                    "end": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, None, None, None, None, None, "0.0000001"
                    ),
                },
            ),
            (
                "PT2.0000048S/2018-03-06",
                {
                    "end": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, None, None, None, None, None, "2.0000048"
                    ),
                },
            ),
            (
                "1981-04-05T01:01:00/P1M1DT1M",
                {
                    "start": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    "duration": DurationTuple(None, "1", None, "1", None, "1", None),
                },
            ),
            (
                "1981-04-05/P1M1D",
                {
                    "start": DateTuple("1981", "04", "05", None, None, None),
                    "duration": DurationTuple(None, "1", None, "1", None, None, None),
                },
            ),
            (
                "2018-03-06/P2,5M",
                {
                    "start": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, "2.5", None, None, None, None, None
                    ),
                },
            ),
            (
                "2018-03-06/P2.5M",
                {
                    "start": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, "2.5", None, None, None, None, None
                    ),
                },
            ),
            (
                "2014-11-12/PT1H",
                {
                    "start": DateTuple("2014", "11", "12", None, None, None),
                    "duration": DurationTuple(None, None, None, None, "1", None, None),
                },
            ),
            (
                "2014-11-12/PT4H54M6.5S",
                {
                    "start": DateTuple("2014", "11", "12", None, None, None),
                    "duration": DurationTuple(None, None, None, None, "4", "54", "6.5"),
                },
            ),
            # Make sure we truncate, not round
            # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
            (
                "2018-03-06/PT0.0000001S",
                {
                    "start": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, None, None, None, None, None, "0.0000001"
                    ),
                },
            ),
            (
                "2018-03-06/PT2.0000048S",
                {
                    "start": DateTuple("2018", "03", "06", None, None, None),
                    "duration": DurationTuple(
                        None, None, None, None, None, None, "2.0000048"
                    ),
                },
            ),
            (
                "1980-03-05T01:01:00/1981-04-05T01:01:00",
                {
                    "start": DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    "end": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                },
            ),
            (
                "1980-03-05T01:01:00/1981-04-05",
                {
                    "start": DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    "end": DateTuple("1981", "04", "05", None, None, None),
                },
            ),
            (
                "1980-03-05/1981-04-05T01:01:00",
                {
                    "start": DateTuple("1980", "03", "05", None, None, None),
                    "end": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                },
            ),
            (
                "1980-03-05/1981-04-05",
                {
                    "start": DateTuple("1980", "03", "05", None, None, None),
                    "end": DateTuple("1981", "04", "05", None, None, None),
                },
            ),
            (
                "1981-04-05/1980-03-05",
                {
                    "start": DateTuple("1981", "04", "05", None, None, None),
                    "end": DateTuple("1980", "03", "05", None, None, None),
                },
            ),
            # Test concise interval
            (
                "2020-01-01/02",
                {
                    "start": DateTuple("2020", "01", "01", None, None, None),
                    "end": DateTuple(None, None, "02", None, None, None),
                },
            ),
            (
                "2008-02-15/03-14",
                {
                    "start": DateTuple("2008", "02", "15", None, None, None),
                    "end": DateTuple(None, "03", "14", None, None, None),
                },
            ),
            (
                "2007-12-14T13:30/15:30",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "12", "14", None, None, None),
                        TimeTuple("13", "30", None, None),
                    ),
                    "end": TimeTuple("15", "30", None, None),
                },
            ),
            (
                "2007-11-13T09:00/15T17:00",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "11", "13", None, None, None),
                        TimeTuple("09", "00", None, None),
                    ),
                    "end": DatetimeTuple(
                        DateTuple(None, None, "15", None, None, None),
                        TimeTuple("17", "00", None, None),
                    ),
                },
            ),
            (
                "2007-11-13T00:00/16T00:00",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "11", "13", None, None, None),
                        TimeTuple("00", "00", None, None),
                    ),
                    "end": DatetimeTuple(
                        DateTuple(None, None, "16", None, None, None),
                        TimeTuple("00", "00", None, None),
                    ),
                },
            ),
            (
                "2007-11-13T09:00Z/15T17:00",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "11", "13", None, None, None),
                        TimeTuple(
                            "09",
                            "00",
                            None,
                            TimezoneTuple(False, True, None, None, "Z"),
                        ),
                    ),
                    "end": DatetimeTuple(
                        DateTuple(None, None, "15", None, None, None),
                        TimeTuple("17", "00", None, None),
                    ),
                },
            ),
            (
                "2007-11-13T00:00/12:34.567",
                {
                    "start": DatetimeTuple(
                        DateTuple("2007", "11", "13", None, None, None),
                        TimeTuple("00", "00", None, None),
                    ),
                    "end": TimeTuple("12", "34.567", None, None),
                },
            ),
            # Make sure we truncate, not round
            # https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is
            (
                "1980-03-05T01:01:00.0000001/" "1981-04-05T14:43:59.9999997",
                {
                    "start": DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00.0000001", None),
                    ),
                    "end": DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("14", "43", "59.9999997", None),
                    ),
                },
            ),
        )

        for testtuple in testtuples:
            mockBuilder = mock.Mock()
            mockBuilder.build_interval.return_value = testtuple[1]

            result = _parse_interval(testtuple[0], mockBuilder)

            self.assertEqual(result, testtuple[1])
            mockBuilder.build_interval.assert_called_once_with(**testtuple[1])

        # Test different separators
        expectedargs = {
            "start": DatetimeTuple(
                DateTuple("1980", "03", "05", None, None, None),
                TimeTuple("01", "01", "00", None),
            ),
            "end": DatetimeTuple(
                DateTuple("1981", "04", "05", None, None, None),
                TimeTuple("01", "01", "00", None),
            ),
        }

        mockBuilder = mock.Mock()
        mockBuilder.build_interval.return_value = expectedargs

        result = _parse_interval(
            "1980-03-05T01:01:00--1981-04-05T01:01:00",
            mockBuilder,
            intervaldelimiter="--",
        )

        self.assertEqual(result, expectedargs)
        mockBuilder.build_interval.assert_called_once_with(**expectedargs)

        expectedargs = {
            "start": DatetimeTuple(
                DateTuple("1980", "03", "05", None, None, None),
                TimeTuple("01", "01", "00", None),
            ),
            "end": DatetimeTuple(
                DateTuple("1981", "04", "05", None, None, None),
                TimeTuple("01", "01", "00", None),
            ),
        }

        mockBuilder = mock.Mock()
        mockBuilder.build_interval.return_value = expectedargs

        _parse_interval(
            "1980-03-05 01:01:00/1981-04-05 01:01:00",
            mockBuilder,
            datetimedelimiter=" ",
        )

        self.assertEqual(result, expectedargs)
        mockBuilder.build_interval.assert_called_once_with(**expectedargs)

    def test_parse_interval_end(self):
        self.assertEqual(
            _parse_interval_end(
                "02", DateTuple("2020", "01", "01", None, None, None), "T"
            ),
            DateTuple(None, None, "02", None, None, None),
        )

        self.assertEqual(
            _parse_interval_end(
                "03-14", DateTuple("2008", "02", "15", None, None, None), "T"
            ),
            DateTuple(None, "03", "14", None, None, None),
        )

        self.assertEqual(
            _parse_interval_end(
                "0314", DateTuple("2008", "02", "15", None, None, None), "T"
            ),
            DateTuple(None, "03", "14", None, None, None),
        )

        self.assertEqual(
            _parse_interval_end(
                "15:30",
                DatetimeTuple(
                    DateTuple("2007", "12", "14", None, None, None),
                    TimeTuple("13", "30", None, None),
                ),
                "T",
            ),
            TimeTuple("15", "30", None, None),
        )

        self.assertEqual(
            _parse_interval_end(
                "15T17:00",
                DatetimeTuple(
                    DateTuple("2007", "11", "13", None, None, None),
                    TimeTuple("09", "00", None, None),
                ),
                "T",
            ),
            DatetimeTuple(
                DateTuple(None, None, "15", None, None, None),
                TimeTuple("17", "00", None, None),
            ),
        )

        self.assertEqual(
            _parse_interval_end(
                "16T00:00",
                DatetimeTuple(
                    DateTuple("2007", "11", "13", None, None, None),
                    TimeTuple("00", "00", None, None),
                ),
                "T",
            ),
            DatetimeTuple(
                DateTuple(None, None, "16", None, None, None),
                TimeTuple("00", "00", None, None),
            ),
        )

        self.assertEqual(
            _parse_interval_end(
                "15 17:00",
                DatetimeTuple(
                    DateTuple("2007", "11", "13", None, None, None),
                    TimeTuple("09", "00", None, None),
                ),
                " ",
            ),
            DatetimeTuple(
                DateTuple(None, None, "15", None, None, None),
                TimeTuple("17", "00", None, None),
            ),
        )

        self.assertEqual(
            _parse_interval_end(
                "12:34.567",
                DatetimeTuple(
                    DateTuple("2007", "11", "13", None, None, None),
                    TimeTuple("00", "00", None, None),
                ),
                "T",
            ),
            TimeTuple("12", "34.567", None, None),
        )


class TestRepeatingIntervalParserFunctions(unittest.TestCase):
    def test_get_interval_resolution_date(self):
        self.assertEqual(
            get_repeating_interval_resolution("R/P1.5Y/2018"), IntervalResolution.Year
        )
        self.assertEqual(
            get_repeating_interval_resolution("R1/P1.5Y/2018-03"),
            IntervalResolution.Month,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R2/P1.5Y/2018-03-06"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R3/P1.5Y/2018W01"),
            IntervalResolution.Week,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R4/P1.5Y/2018-306"),
            IntervalResolution.Ordinal,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R5/P1.5Y/2018W012"),
            IntervalResolution.Weekday,
        )

        self.assertEqual(
            get_repeating_interval_resolution("R/2018/P1.5Y"), IntervalResolution.Year
        )
        self.assertEqual(
            get_repeating_interval_resolution("R1/2018-03/P1.5Y"),
            IntervalResolution.Month,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R2/2018-03-06/P1.5Y"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R3/2018W01/P1.5Y"),
            IntervalResolution.Week,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R4/2018-306/P1.5Y"),
            IntervalResolution.Ordinal,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R5/2018W012/P1.5Y"),
            IntervalResolution.Weekday,
        )

    def test_get_interval_resolution_time(self):
        self.assertEqual(
            get_repeating_interval_resolution("R/P1M/1981-04-05T01"),
            IntervalResolution.Hours,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R1/P1M/1981-04-05T01:01"),
            IntervalResolution.Minutes,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R2/P1M/1981-04-05T01:01:00"),
            IntervalResolution.Seconds,
        )

        self.assertEqual(
            get_repeating_interval_resolution("R/1981-04-05T01/P1M"),
            IntervalResolution.Hours,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R1/1981-04-05T01:01/P1M"),
            IntervalResolution.Minutes,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R2/1981-04-05T01:01:00/P1M"),
            IntervalResolution.Seconds,
        )

    def test_get_interval_resolution_duration(self):
        self.assertEqual(
            get_repeating_interval_resolution("R/2014-11-12/P1Y2M3D"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R1/2014-11-12/P1Y2M"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R2/2014-11-12/P1Y"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R3/2014-11-12/P1W"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R4/2014-11-12/P1Y2M3DT4H"),
            IntervalResolution.Hours,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R5/2014-11-12/P1Y2M3DT4H54M"),
            IntervalResolution.Minutes,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R6/2014-11-12/P1Y2M3DT4H54M6S"),
            IntervalResolution.Seconds,
        )

        self.assertEqual(
            get_repeating_interval_resolution("R/P1Y2M3D/2014-11-12"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R1/P1Y2M/2014-11-12"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R2/P1Y/2014-11-12"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R3/P1W/2014-11-12"),
            IntervalResolution.Day,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R4/P1Y2M3DT4H/2014-11-12"),
            IntervalResolution.Hours,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R5/P1Y2M3DT4H54M/2014-11-12"),
            IntervalResolution.Minutes,
        )
        self.assertEqual(
            get_repeating_interval_resolution("R6/P1Y2M3DT4H54M6S/2014-11-12"),
            IntervalResolution.Seconds,
        )

    def test_parse_repeating_interval(self):
        with mock.patch.object(
            aniso8601.interval.PythonTimeBuilder, "build_repeating_interval"
        ) as mockBuilder:
            expectedargs = {
                "R": False,
                "Rnn": "3",
                "interval": IntervalTuple(
                    DateTuple("1981", "04", "05", None, None, None),
                    None,
                    DurationTuple(None, None, None, "1", None, None, None),
                ),
            }

            mockBuilder.return_value = expectedargs

            result = parse_repeating_interval("R3/1981-04-05/P1D")

            self.assertEqual(result, expectedargs)
            mockBuilder.assert_called_once_with(**expectedargs)

        with mock.patch.object(
            aniso8601.interval.PythonTimeBuilder, "build_repeating_interval"
        ) as mockBuilder:
            expectedargs = {
                "R": False,
                "Rnn": "11",
                "interval": IntervalTuple(
                    None,
                    DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    DurationTuple(None, None, None, None, "1", "2", None),
                ),
            }

            mockBuilder.return_value = expectedargs

            result = parse_repeating_interval("R11/PT1H2M/1980-03-05T01:01:00")

            self.assertEqual(result, expectedargs)
            mockBuilder.assert_called_once_with(**expectedargs)

        with mock.patch.object(
            aniso8601.interval.PythonTimeBuilder, "build_repeating_interval"
        ) as mockBuilder:
            expectedargs = {
                "R": False,
                "Rnn": "2",
                "interval": IntervalTuple(
                    DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    None,
                ),
            }

            mockBuilder.return_value = expectedargs

            result = parse_repeating_interval(
                "R2--1980-03-05T01:01:00--" "1981-04-05T01:01:00",
                intervaldelimiter="--",
            )

            self.assertEqual(result, expectedargs)
            mockBuilder.assert_called_once_with(**expectedargs)

        with mock.patch.object(
            aniso8601.interval.PythonTimeBuilder, "build_repeating_interval"
        ) as mockBuilder:
            expectedargs = {
                "R": False,
                "Rnn": "2",
                "interval": IntervalTuple(
                    DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    DatetimeTuple(
                        DateTuple("1981", "04", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    None,
                ),
            }

            mockBuilder.return_value = expectedargs

            result = parse_repeating_interval(
                "R2/" "1980-03-05 01:01:00/" "1981-04-05 01:01:00",
                datetimedelimiter=" ",
            )

            self.assertEqual(result, expectedargs)
            mockBuilder.assert_called_once_with(**expectedargs)

        with mock.patch.object(
            aniso8601.interval.PythonTimeBuilder, "build_repeating_interval"
        ) as mockBuilder:
            expectedargs = {
                "R": True,
                "Rnn": None,
                "interval": IntervalTuple(
                    None,
                    DatetimeTuple(
                        DateTuple("1980", "03", "05", None, None, None),
                        TimeTuple("01", "01", "00", None),
                    ),
                    DurationTuple(None, None, None, None, "1", "2", None),
                ),
            }

            mockBuilder.return_value = expectedargs

            result = parse_repeating_interval("R/PT1H2M/1980-03-05T01:01:00")

            self.assertEqual(result, expectedargs)
            mockBuilder.assert_called_once_with(**expectedargs)

    def test_parse_repeating_interval_mockbuilder(self):
        mockBuilder = mock.Mock()

        args = {
            "R": False,
            "Rnn": "3",
            "interval": IntervalTuple(
                DateTuple("1981", "04", "05", None, None, None),
                None,
                DurationTuple(None, None, None, "1", None, None, None),
            ),
        }

        mockBuilder.build_repeating_interval.return_value = args

        result = parse_repeating_interval("R3/1981-04-05/P1D", builder=mockBuilder)

        self.assertEqual(result, args)
        mockBuilder.build_repeating_interval.assert_called_once_with(**args)

        mockBuilder = mock.Mock()

        args = {
            "R": False,
            "Rnn": "11",
            "interval": IntervalTuple(
                None,
                DatetimeTuple(
                    DateTuple("1980", "03", "05", None, None, None),
                    TimeTuple("01", "01", "00", None),
                ),
                DurationTuple(None, None, None, None, "1", "2", None),
            ),
        }

        mockBuilder.build_repeating_interval.return_value = args

        result = parse_repeating_interval(
            "R11/PT1H2M/1980-03-05T01:01:00", builder=mockBuilder
        )

        self.assertEqual(result, args)
        mockBuilder.build_repeating_interval.assert_called_once_with(**args)

        mockBuilder = mock.Mock()

        args = {
            "R": True,
            "Rnn": None,
            "interval": IntervalTuple(
                None,
                DatetimeTuple(
                    DateTuple("1980", "03", "05", None, None, None),
                    TimeTuple("01", "01", "00", None),
                ),
                DurationTuple(None, None, None, None, "1", "2", None),
            ),
        }

        mockBuilder.build_repeating_interval.return_value = args

        result = parse_repeating_interval(
            "R/PT1H2M/1980-03-05T01:01:00", builder=mockBuilder
        )

        self.assertEqual(result, args)
        mockBuilder.build_repeating_interval.assert_called_once_with(**args)

    def test_parse_repeating_interval_badtype(self):
        testtuples = (None, 1, False, 1.234)

        for testtuple in testtuples:
            with self.assertRaises(ValueError):
                parse_repeating_interval(testtuple, builder=None)

    def test_parse_repeating_interval_baddelimiter(self):
        testtuples = ("R,PT1H2M,1980-03-05T01:01:00", "R3 1981-04-05 P1D")

        for testtuple in testtuples:
            with self.assertRaises(ISOFormatError):
                parse_repeating_interval(testtuple, builder=None)

    def test_parse_repeating_interval_suffixgarbage(self):
        # Don't allow garbage after the duration
        # https://bitbucket.org/nielsenb/aniso8601/issues/9/durations-with-trailing-garbage-are-parsed
        with self.assertRaises(ISOFormatError):
            parse_repeating_interval("R3/1981-04-05/P1Dasdf", builder=None)

        with self.assertRaises(ISOFormatError):
            parse_repeating_interval(
                "R3/" "1981-04-05/" "P0003-06-04T12:30:05.5asdfasdf", builder=None
            )

    def test_parse_repeating_interval_badstr(self):
        testtuples = ("bad", "")

        for testtuple in testtuples:
            with self.assertRaises(ISOFormatError):
                parse_repeating_interval(testtuple, builder=None)
