Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

edgify / persistent   python

Repository URL to install this package:

/ tests / test_timestamp.py

##############################################################################
#
# Copyright (c) 2011 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import unittest
from contextlib import contextmanager

from persistent.tests.utils import skipIfNoCExtension



class Test__UTC(unittest.TestCase):

    def _getTargetClass(self):
        from persistent.timestamp import _UTC
        return _UTC

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_tzname(self):
        utc = self._makeOne()
        # datetime.timezone.utc changed the tzname in 3.6
        # Now it is just 'UTC', but prior to that it was 'UTC+00:00'
        self.assertEqual(utc.tzname(None)[0:3], 'UTC')

    def test_utcoffset(self):
        from datetime import timedelta
        utc = self._makeOne()
        self.assertEqual(utc.utcoffset(None), timedelta(0))

    def test_dst(self):
        utc = self._makeOne()
        self.assertEqual(utc.dst(None), None)

    def test_fromutc(self):
        import datetime
        source = datetime.datetime.now(self._getTargetClass()())
        utc = self._makeOne()
        self.assertEqual(utc.fromutc(source), source)


class Test__UTCClass(Test__UTC):

    def _getTargetClass(self):
        from persistent.timestamp import _UTCClass
        return _UTCClass


class TimeStampTestsMixin(object):
    # Tests that work for either implementation.

    def _getTargetClass(self):
        raise NotImplementedError

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_ctor_invalid_arglist(self):
        BAD_ARGS = [(),
                    (1,),
                    (1, 2),
                    (1, 2, 3),
                    (1, 2, 3, 4),
                    (1, 2, 3, 4, 5),
                    ('1', '2', '3', '4', '5', '6'),
                    (1, 2, 3, 4, 5, 6, 7),
                    (b'123',),
                   ]
        for args in BAD_ARGS:
            with self.assertRaises((TypeError, ValueError)):
                self._makeOne(*args)

    def test_ctor_from_invalid_strings(self):
        BAD_ARGS = [''
                    '\x00',
                    '\x00' * 2,
                    '\x00' * 3,
                    '\x00' * 4,
                    '\x00' * 5,
                    '\x00' * 7,
                   ]
        for args in BAD_ARGS:
            self.assertRaises((TypeError, ValueError), self._makeOne, *args)

    def test_ctor_from_string(self):
        from persistent.timestamp import _makeUTC
        ZERO = _makeUTC(1900, 1, 1, 0, 0, 0)
        EPOCH = _makeUTC(1970, 1, 1, 0, 0, 0)
        DELTA = ZERO - EPOCH
        DELTA_SECS = DELTA.days * 86400 + DELTA.seconds
        SERIAL = b'\x00' * 8
        ts = self._makeOne(SERIAL)
        self.assertEqual(ts.raw(), SERIAL)
        self.assertEqual(ts.year(), 1900)
        self.assertEqual(ts.month(), 1)
        self.assertEqual(ts.day(), 1)
        self.assertEqual(ts.hour(), 0)
        self.assertEqual(ts.minute(), 0)
        self.assertEqual(ts.second(), 0.0)
        self.assertEqual(ts.timeTime(), DELTA_SECS)

    def test_ctor_from_string_non_zero(self):
        before = self._makeOne(2011, 2, 16, 14, 37, 22.80544)
        after = self._makeOne(before.raw())
        self.assertEqual(before.raw(), after.raw())
        self.assertEqual(before.timeTime(), 1297867042.80544)

    def test_ctor_from_elements(self):
        from persistent.timestamp import _makeUTC
        ZERO = _makeUTC(1900, 1, 1, 0, 0, 0)
        EPOCH = _makeUTC(1970, 1, 1, 0, 0, 0)
        DELTA = ZERO - EPOCH
        DELTA_SECS = DELTA.days * 86400 + DELTA.seconds
        SERIAL = b'\x00' * 8
        ts = self._makeOne(1900, 1, 1, 0, 0, 0.0)
        self.assertEqual(ts.raw(), SERIAL)
        self.assertEqual(ts.year(), 1900)
        self.assertEqual(ts.month(), 1)
        self.assertEqual(ts.day(), 1)
        self.assertEqual(ts.hour(), 0)
        self.assertEqual(ts.minute(), 0)
        self.assertEqual(ts.second(), 0.0)
        self.assertEqual(ts.timeTime(), DELTA_SECS)

    def test_laterThan_invalid(self):
        ERRORS = (ValueError, TypeError)
        SERIAL = b'\x01' * 8
        ts = self._makeOne(SERIAL)
        self.assertRaises(ERRORS, ts.laterThan, None)
        self.assertRaises(ERRORS, ts.laterThan, '')
        self.assertRaises(ERRORS, ts.laterThan, ())
        self.assertRaises(ERRORS, ts.laterThan, [])
        self.assertRaises(ERRORS, ts.laterThan, {})
        self.assertRaises(ERRORS, ts.laterThan, object())

    def test_laterThan_self_is_earlier(self):
        SERIAL1 = b'\x01' * 8
        SERIAL2 = b'\x02' * 8
        ts1 = self._makeOne(SERIAL1)
        ts2 = self._makeOne(SERIAL2)
        later = ts1.laterThan(ts2)
        self.assertEqual(later.raw(), b'\x02' * 7 + b'\x03')

    def test_laterThan_self_is_later(self):
        SERIAL1 = b'\x01' * 8
        SERIAL2 = b'\x02' * 8
        ts1 = self._makeOne(SERIAL1)
        ts2 = self._makeOne(SERIAL2)
        later = ts2.laterThan(ts1)
        self.assertTrue(later is ts2)

    def test_repr(self):
        SERIAL = b'\x01' * 8
        ts = self._makeOne(SERIAL)
        self.assertEqual(repr(ts), repr(SERIAL))

    def test_comparisons_to_non_timestamps(self):
        import operator
        from persistent._compat import PYTHON2
        # Check the corner cases when comparing non-comparable types
        ts = self._makeOne(2011, 2, 16, 14, 37, 22.0)

        def check_common(op, passes):
            if passes == 'neither':
                self.assertFalse(op(ts, None))
                self.assertFalse(op(None, ts))
                return True

            if passes == 'both':
                self.assertTrue(op(ts, None))
                self.assertTrue(op(None, ts))
                return True
            return False

        def check_py2(op, passes): # pragma: no cover
            if passes == 'first':
                self.assertTrue(op(ts, None))
                self.assertFalse(op(None, ts))
            else:
                self.assertFalse(op(ts, None))
                self.assertTrue(op(None, ts))

        def check_py3(op, passes):
            self.assertRaises(TypeError, op, ts, None)
            self.assertRaises(TypeError, op, None, ts)

        check = check_py2 if PYTHON2 else check_py3

        for op_name, passes in (('lt', 'second'),
                                ('gt', 'first'),
                                ('le', 'second'),
                                ('ge', 'first'),
                                ('eq', 'neither'),
                                ('ne', 'both')):
            op = getattr(operator, op_name)
            if not check_common(op, passes):
                check(op, passes)


class Instant(object):
    # Namespace to hold some constants.

    # A particular instant in time.
    now = 1229959248.3

    # That instant in time split as the result of this expression:
    #    (time.gmtime(now)[:5] + (now % 60,))
    now_ts_args = (2008, 12, 22, 15, 20, 48.299999952316284)

    # We happen to know that on a 32-bit platform, the hashcode
    # of a TimeStamp at that instant should be exactly
    # -1419374591
    # and the 64-bit should be exactly:
    # -3850693964765720575
    bit_32_hash = -1419374591
    bit_64_hash = -3850693964765720575

    MAX_32_BITS = 2 ** 31 - 1
    MAX_64_BITS = 2 ** 63 - 1

    def __init__(self):
        from persistent import timestamp as MUT
        self.MUT = MUT
        # pylint:disable=protected-access
        self.orig_maxint = MUT._MAXINT

        self.is_32_bit_hash = self.orig_maxint == self.MAX_32_BITS

        self.orig_c_long = None
        self.c_int64 = None
        self.c_int32 = None
        if MUT.c_long is not None:
            import ctypes
            self.orig_c_long = MUT.c_long
            self.c_int32 = ctypes.c_int32
            self.c_int64 = ctypes.c_int64
            # win32, even on 64-bit long, has funny sizes
            self.is_32_bit_hash = self.c_int32 == ctypes.c_long
        self.expected_hash = self.bit_32_hash if self.is_32_bit_hash else self.bit_64_hash

    @contextmanager
    def _use_hash(self, maxint, c_long):
        # pylint:disable=protected-access
        try:
            self.MUT._MAXINT = maxint
            self.MUT.c_long = c_long
            yield
        finally:
            self.MUT._MAXINT = self.orig_maxint
            self.MUT.c_long = self.orig_c_long


    def use_32bit(self):
        return self._use_hash(self.MAX_32_BITS, self.c_int32)

    def use_64bit(self):
        return self._use_hash(self.MAX_64_BITS, self.c_int64)


class pyTimeStampTests(TimeStampTestsMixin, unittest.TestCase):
    # Tests specific to the Python implementation

    def _getTargetClass(self):
        from persistent.timestamp import TimeStampPy
        return TimeStampPy

    def test_py_hash_32_64_bit(self):
        # Fake out the python version to think it's on a 32-bit
        # platform and test the same; also verify 64 bit
        instant = Instant()

        with instant.use_32bit():
            py = self._makeOne(*Instant.now_ts_args)
            self.assertEqual(hash(py), Instant.bit_32_hash)

        with instant.use_64bit():
            # call __hash__ directly to avoid interpreter truncation
            # in hash() on 32-bit platforms
            self.assertEqual(py.__hash__(), Instant.bit_64_hash)

        self.assertEqual(py.__hash__(), instant.expected_hash)


class CTimeStampTests(TimeStampTestsMixin, unittest.TestCase):

    def _getTargetClass(self):
        from persistent.timestamp import TimeStamp
        return TimeStamp

    def test_hash_32_or_64_bit(self):
        ts = self._makeOne(*Instant.now_ts_args)
        self.assertIn(hash(ts), (Instant.bit_32_hash, Instant.bit_64_hash))


@skipIfNoCExtension
class PyAndCComparisonTests(unittest.TestCase):
    """
    Compares C and Python implementations.
    """

    def _make_many_instants(self):
        # Given the above data, return many slight variations on
        # it to test matching
        yield Instant.now_ts_args
        for i in range(2000):
            yield Instant.now_ts_args[:-1] + (Instant.now_ts_args[-1] + (i % 60.0)/100.0, )

    def _makeC(self, *args, **kwargs):
        from persistent._compat import _c_optimizations_available as get_c
        return get_c()['persistent.timestamp'].TimeStamp(*args, **kwargs)

    def _makePy(self, *args, **kwargs):
        from persistent.timestamp import TimeStampPy
        return TimeStampPy(*args, **kwargs)

    def _make_C_and_Py(self, *args, **kwargs):
        return self._makeC(*args, **kwargs), self._makePy(*args, **kwargs)

    def test_reprs_equal(self):
        for args in self._make_many_instants():
            c, py = self._make_C_and_Py(*args)
            self.assertEqual(repr(c), repr(py))

    def test_strs_equal(self):
        for args in self._make_many_instants():
            c, py = self._make_C_and_Py(*args)
            self.assertEqual(str(c), str(py))

    def test_raw_equal(self):
        c, py = self._make_C_and_Py(*Instant.now_ts_args)
        self.assertEqual(c.raw(), py.raw())

    def test_equal(self):
        c, py = self._make_C_and_Py(*Instant.now_ts_args)
        self.assertEqual(c, py)
Loading ...