# Copyright (c) 2010-2023 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for swift.common.utils.timestamp""" import random import time import unittest import mock from swift.common.utils import timestamp class TestTimestamp(unittest.TestCase): """Tests for swift.common.utils.timestamp.Timestamp""" def test_invalid_input(self): with self.assertRaises(ValueError): timestamp.Timestamp(time.time(), offset=-1) with self.assertRaises(ValueError): timestamp.Timestamp('123.456_78_90') def test_invalid_string_conversion(self): t = timestamp.Timestamp.now() self.assertRaises(TypeError, str, t) def test_offset_limit(self): t = 1417462430.78693 # can't have a offset above MAX_OFFSET with self.assertRaises(ValueError): timestamp.Timestamp(t, offset=timestamp.MAX_OFFSET + 1) # exactly max offset is fine ts = timestamp.Timestamp(t, offset=timestamp.MAX_OFFSET) self.assertEqual(ts.internal, '1417462430.78693_ffffffffffffffff') # but you can't offset it further with self.assertRaises(ValueError): timestamp.Timestamp(ts.internal, offset=1) # unless you start below it ts = timestamp.Timestamp(t, offset=timestamp.MAX_OFFSET - 1) self.assertEqual(timestamp.Timestamp(ts.internal, offset=1), '1417462430.78693_ffffffffffffffff') def test_normal_format_no_offset(self): expected = '1402436408.91203' test_values = ( '1402436408.91203', '1402436408.91203_00000000', '1402436408.912030000', '1402436408.912030000_0000000000000', '000001402436408.912030000', '000001402436408.912030000_0000000000', 1402436408.91203, 1402436408.912029, 1402436408.9120300000000000, 1402436408.91202999999999999, timestamp.Timestamp(1402436408.91203), timestamp.Timestamp(1402436408.91203, offset=0), timestamp.Timestamp(1402436408.912029), timestamp.Timestamp(1402436408.912029, offset=0), timestamp.Timestamp('1402436408.91203'), timestamp.Timestamp('1402436408.91203', offset=0), timestamp.Timestamp('1402436408.91203_00000000'), timestamp.Timestamp('1402436408.91203_00000000', offset=0), ) for value in test_values: ts = timestamp.Timestamp(value) self.assertEqual(ts.normal, expected) # timestamp instance can also compare to string or float self.assertEqual(ts, expected) self.assertEqual(ts, float(expected)) self.assertEqual(ts, timestamp.normalize_timestamp(expected)) def test_isoformat(self): expected = '2014-06-10T22:47:32.054580' test_values = ( '1402440452.05458', '1402440452.054579', '1402440452.05458_00000000', '1402440452.054579_00000000', '1402440452.054580000', '1402440452.054579999', '1402440452.054580000_0000000000000', '1402440452.054579999_0000ff00', '000001402440452.054580000', '000001402440452.0545799', '000001402440452.054580000_0000000000', '000001402440452.054579999999_00000fffff', 1402440452.05458, 1402440452.054579, 1402440452.0545800000000000, 1402440452.054579999, timestamp.Timestamp(1402440452.05458), timestamp.Timestamp(1402440452.0545799), timestamp.Timestamp(1402440452.05458, offset=0), timestamp.Timestamp(1402440452.05457999999, offset=0), timestamp.Timestamp(1402440452.05458, offset=100), timestamp.Timestamp(1402440452.054579, offset=100), timestamp.Timestamp('1402440452.05458'), timestamp.Timestamp('1402440452.054579999'), timestamp.Timestamp('1402440452.05458', offset=0), timestamp.Timestamp('1402440452.054579', offset=0), timestamp.Timestamp('1402440452.05458', offset=300), timestamp.Timestamp('1402440452.05457999', offset=300), timestamp.Timestamp('1402440452.05458_00000000'), timestamp.Timestamp('1402440452.05457999_00000000'), timestamp.Timestamp('1402440452.05458_00000000', offset=0), timestamp.Timestamp('1402440452.05457999_00000aaa', offset=0), timestamp.Timestamp('1402440452.05458_00000000', offset=400), timestamp.Timestamp('1402440452.054579_0a', offset=400), ) for value in test_values: self.assertEqual(timestamp.Timestamp(value).isoformat, expected) expected = '1970-01-01T00:00:00.000000' test_values = ( '0', '0000000000.00000', '0000000000.00000_ffffffffffff', 0, 0.0, ) for value in test_values: self.assertEqual(timestamp.Timestamp(value).isoformat, expected) def test_from_isoformat(self): ts = timestamp.Timestamp.from_isoformat('2014-06-10T22:47:32.054580') self.assertIsInstance(ts, timestamp.Timestamp) self.assertEqual(1402440452.05458, float(ts)) self.assertEqual('2014-06-10T22:47:32.054580', ts.isoformat) ts = timestamp.Timestamp.from_isoformat('1970-01-01T00:00:00.000000') self.assertIsInstance(ts, timestamp.Timestamp) self.assertEqual(0.0, float(ts)) self.assertEqual('1970-01-01T00:00:00.000000', ts.isoformat) ts = timestamp.Timestamp(1402440452.05458) self.assertIsInstance(ts, timestamp.Timestamp) self.assertEqual(ts, timestamp.Timestamp.from_isoformat(ts.isoformat)) def test_ceil(self): self.assertEqual(0.0, timestamp.Timestamp(0).ceil()) self.assertEqual(1.0, timestamp.Timestamp(0.00001).ceil()) self.assertEqual(1.0, timestamp.Timestamp(0.000001).ceil()) self.assertEqual(12345678.0, timestamp.Timestamp(12345678.0).ceil()) self.assertEqual(12345679.0, timestamp.Timestamp(12345678.000001).ceil()) def test_not_equal(self): ts = '1402436408.91203_0000000000000001' test_values = ( timestamp.Timestamp('1402436408.91203_0000000000000002'), timestamp.Timestamp('1402436408.91203'), timestamp.Timestamp(1402436408.91203), timestamp.Timestamp(1402436408.91204), timestamp.Timestamp(1402436408.91203, offset=0), timestamp.Timestamp(1402436408.91203, offset=2), ) for value in test_values: self.assertTrue(value != ts) self.assertIs(True, timestamp.Timestamp(ts) == ts) # sanity self.assertIs(False, timestamp.Timestamp(ts) != timestamp.Timestamp(ts)) self.assertIs(False, timestamp.Timestamp(ts) != ts) self.assertIs(False, timestamp.Timestamp(ts) is None) self.assertIs(True, timestamp.Timestamp(ts) is not None) def test_no_force_internal_no_offset(self): """Test that internal is the same as normal with no offset""" with mock.patch('swift.common.utils.timestamp.FORCE_INTERNAL', new=False): self.assertEqual(timestamp.Timestamp(0).internal, '0000000000.00000') self.assertEqual(timestamp.Timestamp(1402437380.58186).internal, '1402437380.58186') self.assertEqual(timestamp.Timestamp(1402437380.581859).internal, '1402437380.58186') self.assertEqual(timestamp.Timestamp(0).internal, timestamp.normalize_timestamp(0)) def test_no_force_internal_with_offset(self): """Test that internal always includes the offset if significant""" with mock.patch('swift.common.utils.timestamp.FORCE_INTERNAL', new=False): self.assertEqual(timestamp.Timestamp(0, offset=1).internal, '0000000000.00000_0000000000000001') self.assertEqual( timestamp.Timestamp(1402437380.58186, offset=16).internal, '1402437380.58186_0000000000000010') self.assertEqual( timestamp.Timestamp(1402437380.581859, offset=240).internal, '1402437380.58186_00000000000000f0') self.assertEqual( timestamp.Timestamp('1402437380.581859_00000001', offset=240).internal, '1402437380.58186_00000000000000f1') def test_force_internal(self): """Test that internal always includes the offset if forced""" with mock.patch('swift.common.utils.timestamp.FORCE_INTERNAL', new=True): self.assertEqual(timestamp.Timestamp(0).internal, '0000000000.00000_0000000000000000') self.assertEqual(timestamp.Timestamp(1402437380.58186).internal, '1402437380.58186_0000000000000000') self.assertEqual(timestamp.Timestamp(1402437380.581859).internal, '1402437380.58186_0000000000000000') self.assertEqual(timestamp.Timestamp(0, offset=1).internal, '0000000000.00000_0000000000000001') self.assertEqual( timestamp.Timestamp(1402437380.58186, offset=16).internal, '1402437380.58186_0000000000000010') self.assertEqual( timestamp.Timestamp(1402437380.581859, offset=16).internal, '1402437380.58186_0000000000000010') def test_internal_format_no_offset(self): expected = '1402436408.91203_0000000000000000' test_values = ( '1402436408.91203', '1402436408.91203_00000000', '1402436408.912030000', '1402436408.912030000_0000000000000', '000001402436408.912030000', '000001402436408.912030000_0000000000', 1402436408.91203, 1402436408.9120300000000000, 1402436408.912029, 1402436408.912029999999999999, timestamp.Timestamp(1402436408.91203), timestamp.Timestamp(1402436408.91203, offset=0), timestamp.Timestamp(1402436408.912029), timestamp.Timestamp(1402436408.91202999999999999, offset=0), timestamp.Timestamp('1402436408.91203'), timestamp.Timestamp('1402436408.91203', offset=0), timestamp.Timestamp('1402436408.912029'), timestamp.Timestamp('1402436408.912029', offset=0), timestamp.Timestamp('1402436408.912029999999999'), timestamp.Timestamp('1402436408.912029999999999', offset=0), ) for value in test_values: # timestamp instance is always equivalent self.assertEqual(timestamp.Timestamp(value), expected) if timestamp.FORCE_INTERNAL: # the FORCE_INTERNAL flag makes the internal format always # include the offset portion of the timestamp even when it's # not significant and would be bad during upgrades self.assertEqual(timestamp.Timestamp(value).internal, expected) else: # unless we FORCE_INTERNAL, when there's no offset the # internal format is equivalent to the normalized format self.assertEqual(timestamp.Timestamp(value).internal, '1402436408.91203') def test_internal_format_with_offset(self): expected = '1402436408.91203_00000000000000f0' test_values = ( '1402436408.91203_000000f0', u'1402436408.91203_000000f0', b'1402436408.91203_000000f0', '1402436408.912030000_0000000000f0', '1402436408.912029_000000f0', '1402436408.91202999999_0000000000f0', '000001402436408.912030000_000000000f0', '000001402436408.9120299999_000000000f0', timestamp.Timestamp(1402436408.91203, offset=240), timestamp.Timestamp(1402436408.912029, offset=240), timestamp.Timestamp('1402436408.91203', offset=240), timestamp.Timestamp('1402436408.91203_00000000', offset=240), timestamp.Timestamp('1402436408.91203_0000000f', offset=225), timestamp.Timestamp('1402436408.9120299999', offset=240), timestamp.Timestamp('1402436408.9120299999_00000000', offset=240), timestamp.Timestamp('1402436408.9120299999_00000010', offset=224), ) for value in test_values: ts = timestamp.Timestamp(value) self.assertEqual(ts.internal, expected) # can compare with offset if the string is internalized self.assertEqual(ts, expected) # if comparison value only includes the normalized portion and the # timestamp includes an offset, it is considered greater normal = timestamp.Timestamp(expected).normal self.assertTrue(ts > normal, '%r is not bigger than %r given %r' % ( ts, normal, value)) self.assertTrue(ts > float(normal), '%r is not bigger than %f given %r' % ( ts, float(normal), value)) def test_short_format_with_offset(self): expected = '1402436408.91203_f0' ts = timestamp.Timestamp(1402436408.91203, 0xf0) self.assertEqual(expected, ts.short) expected = '1402436408.91203' ts = timestamp.Timestamp(1402436408.91203) self.assertEqual(expected, ts.short) def test_raw(self): expected = 140243640891203 ts = timestamp.Timestamp(1402436408.91203) self.assertEqual(expected, ts.raw) # 'raw' does not include offset ts = timestamp.Timestamp(1402436408.91203, 0xf0) self.assertEqual(expected, ts.raw) def test_delta(self): def _assertWithinBounds(expected, timestamp): tolerance = 0.00001 minimum = expected - tolerance maximum = expected + tolerance self.assertTrue(float(timestamp) > minimum) self.assertTrue(float(timestamp) < maximum) ts = timestamp.Timestamp(1402436408.91203, delta=100) _assertWithinBounds(1402436408.91303, ts) self.assertEqual(140243640891303, ts.raw) ts = timestamp.Timestamp(1402436408.91203, delta=-100) _assertWithinBounds(1402436408.91103, ts) self.assertEqual(140243640891103, ts.raw) ts = timestamp.Timestamp(1402436408.91203, delta=0) _assertWithinBounds(1402436408.91203, ts) self.assertEqual(140243640891203, ts.raw) # delta is independent of offset ts = timestamp.Timestamp(1402436408.91203, offset=42, delta=100) self.assertEqual(140243640891303, ts.raw) self.assertEqual(42, ts.offset) # cannot go negative self.assertRaises(ValueError, timestamp.Timestamp, 1402436408.91203, delta=-140243640891203) def test_int(self): expected = 1402437965 test_values = ( '1402437965.91203', '1402437965.91203_00000000', '1402437965.912030000', '1402437965.912030000_0000000000000', '000001402437965.912030000', '000001402437965.912030000_0000000000', 1402437965.91203, 1402437965.9120300000000000, 1402437965.912029, 1402437965.912029999999999999, timestamp.Timestamp(1402437965.91203), timestamp.Timestamp(1402437965.91203, offset=0), timestamp.Timestamp(1402437965.91203, offset=500), timestamp.Timestamp(1402437965.912029), timestamp.Timestamp(1402437965.91202999999999999, offset=0), timestamp.Timestamp(1402437965.91202999999999999, offset=300), timestamp.Timestamp('1402437965.91203'), timestamp.Timestamp('1402437965.91203', offset=0), timestamp.Timestamp('1402437965.91203', offset=400), timestamp.Timestamp('1402437965.912029'), timestamp.Timestamp('1402437965.912029', offset=0), timestamp.Timestamp('1402437965.912029', offset=200), timestamp.Timestamp('1402437965.912029999999999'), timestamp.Timestamp('1402437965.912029999999999', offset=0), timestamp.Timestamp('1402437965.912029999999999', offset=100), ) for value in test_values: ts = timestamp.Timestamp(value) self.assertEqual(int(ts), expected) self.assertTrue(ts > expected) def test_float(self): expected = 1402438115.91203 test_values = ( '1402438115.91203', '1402438115.91203_00000000', '1402438115.912030000', '1402438115.912030000_0000000000000', '000001402438115.912030000', '000001402438115.912030000_0000000000', 1402438115.91203, 1402438115.9120300000000000, 1402438115.912029, 1402438115.912029999999999999, timestamp.Timestamp(1402438115.91203), timestamp.Timestamp(1402438115.91203, offset=0), timestamp.Timestamp(1402438115.91203, offset=500), timestamp.Timestamp(1402438115.912029), timestamp.Timestamp(1402438115.91202999999999999, offset=0), timestamp.Timestamp(1402438115.91202999999999999, offset=300), timestamp.Timestamp('1402438115.91203'), timestamp.Timestamp('1402438115.91203', offset=0), timestamp.Timestamp('1402438115.91203', offset=400), timestamp.Timestamp('1402438115.912029'), timestamp.Timestamp('1402438115.912029', offset=0), timestamp.Timestamp('1402438115.912029', offset=200), timestamp.Timestamp('1402438115.912029999999999'), timestamp.Timestamp('1402438115.912029999999999', offset=0), timestamp.Timestamp('1402438115.912029999999999', offset=100), ) tolerance = 0.00001 minimum = expected - tolerance maximum = expected + tolerance for value in test_values: ts = timestamp.Timestamp(value) self.assertTrue(float(ts) > minimum, '%f is not bigger than %f given %r' % ( ts, minimum, value)) self.assertTrue(float(ts) < maximum, '%f is not smaller than %f given %r' % ( ts, maximum, value)) # direct comparison of timestamp works too self.assertTrue(ts > minimum, '%s is not bigger than %f given %r' % ( ts.normal, minimum, value)) self.assertTrue(ts < maximum, '%s is not smaller than %f given %r' % ( ts.normal, maximum, value)) # ... even against strings self.assertTrue(ts > '%f' % minimum, '%s is not bigger than %s given %r' % ( ts.normal, minimum, value)) self.assertTrue(ts < '%f' % maximum, '%s is not smaller than %s given %r' % ( ts.normal, maximum, value)) def test_false(self): self.assertFalse(timestamp.Timestamp(0)) self.assertFalse(timestamp.Timestamp(0, offset=0)) self.assertFalse(timestamp.Timestamp('0')) self.assertFalse(timestamp.Timestamp('0', offset=0)) self.assertFalse(timestamp.Timestamp(0.0)) self.assertFalse(timestamp.Timestamp(0.0, offset=0)) self.assertFalse(timestamp.Timestamp('0.0')) self.assertFalse(timestamp.Timestamp('0.0', offset=0)) self.assertFalse(timestamp.Timestamp(00000000.00000000)) self.assertFalse(timestamp.Timestamp(00000000.00000000, offset=0)) self.assertFalse(timestamp.Timestamp('00000000.00000000')) self.assertFalse(timestamp.Timestamp('00000000.00000000', offset=0)) def test_true(self): self.assertTrue(timestamp.Timestamp(1)) self.assertTrue(timestamp.Timestamp(1, offset=1)) self.assertTrue(timestamp.Timestamp(0, offset=1)) self.assertTrue(timestamp.Timestamp('1')) self.assertTrue(timestamp.Timestamp('1', offset=1)) self.assertTrue(timestamp.Timestamp('0', offset=1)) self.assertTrue(timestamp.Timestamp(1.1)) self.assertTrue(timestamp.Timestamp(1.1, offset=1)) self.assertTrue(timestamp.Timestamp(0.0, offset=1)) self.assertTrue(timestamp.Timestamp('1.1')) self.assertTrue(timestamp.Timestamp('1.1', offset=1)) self.assertTrue(timestamp.Timestamp('0.0', offset=1)) self.assertTrue(timestamp.Timestamp(11111111.11111111)) self.assertTrue(timestamp.Timestamp(11111111.11111111, offset=1)) self.assertTrue(timestamp.Timestamp(00000000.00000000, offset=1)) self.assertTrue(timestamp.Timestamp('11111111.11111111')) self.assertTrue(timestamp.Timestamp('11111111.11111111', offset=1)) self.assertTrue(timestamp.Timestamp('00000000.00000000', offset=1)) def test_greater_no_offset(self): now = time.time() older = now - 1 ts = timestamp.Timestamp(now) test_values = ( 0, '0', 0.0, '0.0', '0000.0000', '000.000_000', 1, '1', 1.1, '1.1', '1111.1111', '111.111_111', 1402443112.213252, '1402443112.213252', '1402443112.213252_ffff', older, '%f' % older, '%f_0000ffff' % older, ) for value in test_values: other = timestamp.Timestamp(value) self.assertNotEqual(ts, other) # sanity self.assertTrue(ts > value, '%r is not greater than %r given %r' % ( ts, value, value)) self.assertTrue(ts > other, '%r is not greater than %r given %r' % ( ts, other, value)) self.assertTrue(ts > other.normal, '%r is not greater than %r given %r' % ( ts, other.normal, value)) self.assertTrue(ts > other.internal, '%r is not greater than %r given %r' % ( ts, other.internal, value)) self.assertTrue(ts > float(other), '%r is not greater than %r given %r' % ( ts, float(other), value)) self.assertTrue(ts > int(other), '%r is not greater than %r given %r' % ( ts, int(other), value)) def _test_greater_with_offset(self, now, test_values): for offset in range(1, 1000, 100): ts = timestamp.Timestamp(now, offset=offset) for value in test_values: other = timestamp.Timestamp(value) self.assertNotEqual(ts, other) # sanity self.assertTrue(ts > value, '%r is not greater than %r given %r' % ( ts, value, value)) self.assertTrue(ts > other, '%r is not greater than %r given %r' % ( ts, other, value)) self.assertTrue(ts > other.normal, '%r is not greater than %r given %r' % ( ts, other.normal, value)) self.assertTrue(ts > other.internal, '%r is not greater than %r given %r' % ( ts, other.internal, value)) self.assertTrue(ts > float(other), '%r is not greater than %r given %r' % ( ts, float(other), value)) self.assertTrue(ts > int(other), '%r is not greater than %r given %r' % ( ts, int(other), value)) def test_greater_with_offset(self): # Part 1: use the natural time of the Python. This is deliciously # unpredictable, but completely legitimate and realistic. Finds bugs! now = time.time() older = now - 1 test_values = ( 0, '0', 0.0, '0.0', '0000.0000', '000.000_000', 1, '1', 1.1, '1.1', '1111.1111', '111.111_111', 1402443346.935174, '1402443346.93517', '1402443346.935169_ffff', older, now, ) self._test_greater_with_offset(now, test_values) # Part 2: Same as above, but with fixed time values that reproduce # specific corner cases. now = 1519830570.6949348 older = now - 1 test_values = ( 0, '0', 0.0, '0.0', '0000.0000', '000.000_000', 1, '1', 1.1, '1.1', '1111.1111', '111.111_111', 1402443346.935174, '1402443346.93517', '1402443346.935169_ffff', older, now, ) self._test_greater_with_offset(now, test_values) # Part 3: The '%f' problem. Timestamps cannot be converted to %f # strings, then back to timestamps, then compared with originals. # You can only "import" a floating point representation once. now = 1519830570.6949348 now = float('%f' % now) older = now - 1 test_values = ( 0, '0', 0.0, '0.0', '0000.0000', '000.000_000', 1, '1', 1.1, '1.1', '1111.1111', '111.111_111', older, '%f' % older, '%f_0000ffff' % older, now, '%f' % now, '%s_00000000' % now, ) self._test_greater_with_offset(now, test_values) def test_smaller_no_offset(self): now = time.time() newer = now + 1 ts = timestamp.Timestamp(now) test_values = ( 9999999999.99999, '9999999999.99999', '9999999999.99999_ffff', newer, '%f' % newer, '%f_0000ffff' % newer, ) for value in test_values: other = timestamp.Timestamp(value) self.assertNotEqual(ts, other) # sanity self.assertTrue(ts < value, '%r is not smaller than %r given %r' % ( ts, value, value)) self.assertTrue(ts < other, '%r is not smaller than %r given %r' % ( ts, other, value)) self.assertTrue(ts < other.normal, '%r is not smaller than %r given %r' % ( ts, other.normal, value)) self.assertTrue(ts < other.internal, '%r is not smaller than %r given %r' % ( ts, other.internal, value)) self.assertTrue(ts < float(other), '%r is not smaller than %r given %r' % ( ts, float(other), value)) self.assertTrue(ts < int(other), '%r is not smaller than %r given %r' % ( ts, int(other), value)) def test_smaller_with_offset(self): now = time.time() newer = now + 1 test_values = ( 9999999999.99999, '9999999999.99999', '9999999999.99999_ffff', newer, '%f' % newer, '%f_0000ffff' % newer, ) for offset in range(1, 1000, 100): ts = timestamp.Timestamp(now, offset=offset) for value in test_values: other = timestamp.Timestamp(value) self.assertNotEqual(ts, other) # sanity self.assertTrue(ts < value, '%r is not smaller than %r given %r' % ( ts, value, value)) self.assertTrue(ts < other, '%r is not smaller than %r given %r' % ( ts, other, value)) self.assertTrue(ts < other.normal, '%r is not smaller than %r given %r' % ( ts, other.normal, value)) self.assertTrue(ts < other.internal, '%r is not smaller than %r given %r' % ( ts, other.internal, value)) self.assertTrue(ts < float(other), '%r is not smaller than %r given %r' % ( ts, float(other), value)) self.assertTrue(ts < int(other), '%r is not smaller than %r given %r' % ( ts, int(other), value)) def test_cmp_with_none(self): self.assertGreater(timestamp.Timestamp(0), None) self.assertGreater(timestamp.Timestamp(1.0), None) self.assertGreater(timestamp.Timestamp(1.0, 42), None) def test_ordering(self): given = [ '1402444820.62590_000000000000000a', '1402444820.62589_0000000000000001', '1402444821.52589_0000000000000004', '1402444920.62589_0000000000000004', '1402444821.62589_000000000000000a', '1402444821.72589_000000000000000a', '1402444920.62589_0000000000000002', '1402444820.62589_0000000000000002', '1402444820.62589_000000000000000a', '1402444820.62590_0000000000000004', '1402444920.62589_000000000000000a', '1402444820.62590_0000000000000002', '1402444821.52589_0000000000000002', '1402444821.52589_0000000000000000', '1402444920.62589', '1402444821.62589_0000000000000004', '1402444821.72589_0000000000000001', '1402444820.62590', '1402444820.62590_0000000000000001', '1402444820.62589_0000000000000004', '1402444821.72589_0000000000000000', '1402444821.52589_000000000000000a', '1402444821.72589_0000000000000004', '1402444821.62589', '1402444821.52589_0000000000000001', '1402444821.62589_0000000000000001', '1402444821.62589_0000000000000002', '1402444821.72589_0000000000000002', '1402444820.62589', '1402444920.62589_0000000000000001'] expected = [ '1402444820.62589', '1402444820.62589_0000000000000001', '1402444820.62589_0000000000000002', '1402444820.62589_0000000000000004', '1402444820.62589_000000000000000a', '1402444820.62590', '1402444820.62590_0000000000000001', '1402444820.62590_0000000000000002', '1402444820.62590_0000000000000004', '1402444820.62590_000000000000000a', '1402444821.52589', '1402444821.52589_0000000000000001', '1402444821.52589_0000000000000002', '1402444821.52589_0000000000000004', '1402444821.52589_000000000000000a', '1402444821.62589', '1402444821.62589_0000000000000001', '1402444821.62589_0000000000000002', '1402444821.62589_0000000000000004', '1402444821.62589_000000000000000a', '1402444821.72589', '1402444821.72589_0000000000000001', '1402444821.72589_0000000000000002', '1402444821.72589_0000000000000004', '1402444821.72589_000000000000000a', '1402444920.62589', '1402444920.62589_0000000000000001', '1402444920.62589_0000000000000002', '1402444920.62589_0000000000000004', '1402444920.62589_000000000000000a', ] # less visual version """ now = time.time() given = [ timestamp.Timestamp(now + i, offset=offset).internal for i in (0, 0.00001, 0.9, 1.0, 1.1, 100.0) for offset in (0, 1, 2, 4, 10) ] expected = [t for t in given] random.shuffle(given) """ self.assertEqual(len(given), len(expected)) # sanity timestamps = [timestamp.Timestamp(t) for t in given] # our expected values don't include insignificant offsets with mock.patch('swift.common.utils.timestamp.FORCE_INTERNAL', new=False): self.assertEqual( [t.internal for t in sorted(timestamps)], expected) # string sorting works as well self.assertEqual( sorted([t.internal for t in timestamps]), expected) def test_hashable(self): ts_0 = timestamp.Timestamp('1402444821.72589') ts_0_also = timestamp.Timestamp('1402444821.72589') self.assertEqual(ts_0, ts_0_also) # sanity self.assertEqual(hash(ts_0), hash(ts_0_also)) d = {ts_0: 'whatever'} self.assertIn(ts_0, d) # sanity self.assertIn(ts_0_also, d) def test_out_of_range_comparisons(self): now = timestamp.Timestamp.now() def check_is_later(val): self.assertTrue(now != val) self.assertFalse(now == val) self.assertTrue(now <= val) self.assertTrue(now < val) self.assertTrue(val > now) self.assertTrue(val >= now) check_is_later(1e30) check_is_later(1579753284000) # someone gave us ms instead of s! check_is_later('1579753284000') check_is_later(b'1e15') check_is_later(u'1.e+10_f') def check_is_earlier(val): self.assertTrue(now != val) self.assertFalse(now == val) self.assertTrue(now >= val) self.assertTrue(now > val) self.assertTrue(val < now) self.assertTrue(val <= now) check_is_earlier(-1) check_is_earlier(-0.1) check_is_earlier('-9999999') check_is_earlier(b'-9999.999') check_is_earlier(u'-1234_5678') def test_inversion(self): ts = timestamp.Timestamp(0) self.assertIsInstance(~ts, timestamp.Timestamp) self.assertEqual((~ts).internal, '9999999999.99999') ts = timestamp.Timestamp(123456.789) self.assertIsInstance(~ts, timestamp.Timestamp) self.assertEqual(ts.internal, '0000123456.78900') self.assertEqual((~ts).internal, '9999876543.21099') timestamps = sorted(timestamp.Timestamp(random.random() * 1e10) for _ in range(20)) self.assertEqual([x.internal for x in timestamps], sorted(x.internal for x in timestamps)) self.assertEqual([(~x).internal for x in reversed(timestamps)], sorted((~x).internal for x in timestamps)) ts = timestamp.Timestamp.now() self.assertGreater(~ts, ts) # NB: will break around 2128 ts = timestamp.Timestamp.now(offset=1) with self.assertRaises(ValueError) as caught: ~ts self.assertEqual(caught.exception.args[0], 'Cannot invert timestamps with offsets') class TestTimestampEncoding(unittest.TestCase): def setUp(self): t0 = timestamp.Timestamp(0.0) t1 = timestamp.Timestamp(997.9996) t2 = timestamp.Timestamp(999) t3 = timestamp.Timestamp(1000, 24) t4 = timestamp.Timestamp(1001) t5 = timestamp.Timestamp(1002.00040) # encodings that are expected when explicit = False self.non_explicit_encodings = ( ('0000001000.00000_18', (t3, t3, t3)), ('0000001000.00000_18', (t3, t3, None)), ) # mappings that are expected when explicit = True self.explicit_encodings = ( ('0000001000.00000_18+0+0', (t3, t3, t3)), ('0000001000.00000_18+0', (t3, t3, None)), ) # mappings that are expected when explicit = True or False self.encodings = ( ('0000001000.00000_18+0+186a0', (t3, t3, t4)), ('0000001000.00000_18+186a0+186c8', (t3, t4, t5)), ('0000001000.00000_18-186a0+0', (t3, t2, t2)), ('0000001000.00000_18+0-186a0', (t3, t3, t2)), ('0000001000.00000_18-186a0-186c8', (t3, t2, t1)), ('0000001000.00000_18', (t3, None, None)), ('0000001000.00000_18+186a0', (t3, t4, None)), ('0000001000.00000_18-186a0', (t3, t2, None)), ('0000001000.00000_18', (t3, None, t1)), ('0000001000.00000_18-5f5e100', (t3, t0, None)), ('0000001000.00000_18+0-5f5e100', (t3, t3, t0)), ('0000001000.00000_18-5f5e100+5f45a60', (t3, t0, t2)), ) # decodings that are expected when explicit = False self.non_explicit_decodings = ( ('0000001000.00000_18', (t3, t3, t3)), ('0000001000.00000_18+186a0', (t3, t4, t4)), ('0000001000.00000_18-186a0', (t3, t2, t2)), ('0000001000.00000_18+186a0', (t3, t4, t4)), ('0000001000.00000_18-186a0', (t3, t2, t2)), ('0000001000.00000_18-5f5e100', (t3, t0, t0)), ) # decodings that are expected when explicit = True self.explicit_decodings = ( ('0000001000.00000_18+0+0', (t3, t3, t3)), ('0000001000.00000_18+0', (t3, t3, None)), ('0000001000.00000_18', (t3, None, None)), ('0000001000.00000_18+186a0', (t3, t4, None)), ('0000001000.00000_18-186a0', (t3, t2, None)), ('0000001000.00000_18-5f5e100', (t3, t0, None)), ) # decodings that are expected when explicit = True or False self.decodings = ( ('0000001000.00000_18+0+186a0', (t3, t3, t4)), ('0000001000.00000_18+186a0+186c8', (t3, t4, t5)), ('0000001000.00000_18-186a0+0', (t3, t2, t2)), ('0000001000.00000_18+0-186a0', (t3, t3, t2)), ('0000001000.00000_18-186a0-186c8', (t3, t2, t1)), ('0000001000.00000_18-5f5e100+5f45a60', (t3, t0, t2)), ) def _assertEqual(self, expected, actual, test): self.assertEqual(expected, actual, 'Got %s but expected %s for parameters %s' % (actual, expected, test)) def test_encoding(self): for test in self.explicit_encodings: actual = timestamp.encode_timestamps(test[1][0], test[1][1], test[1][2], True) self._assertEqual(test[0], actual, test[1]) for test in self.non_explicit_encodings: actual = timestamp.encode_timestamps(test[1][0], test[1][1], test[1][2], False) self._assertEqual(test[0], actual, test[1]) for explicit in (True, False): for test in self.encodings: actual = timestamp.encode_timestamps(test[1][0], test[1][1], test[1][2], explicit) self._assertEqual(test[0], actual, test[1]) def test_decoding(self): for test in self.explicit_decodings: actual = timestamp.decode_timestamps(test[0], True) self._assertEqual(test[1], actual, test[0]) for test in self.non_explicit_decodings: actual = timestamp.decode_timestamps(test[0], False) self._assertEqual(test[1], actual, test[0]) for explicit in (True, False): for test in self.decodings: actual = timestamp.decode_timestamps(test[0], explicit) self._assertEqual(test[1], actual, test[0])