
Previously it was possible for a Timestamp to have inconsistent raw and normal values. For example: >>> ts1 = Timestamp(1755077566.523385) >>> (ts1.normal, ts1.raw, (~ts1).normal) ('1755077566.52339', 175507756652338, '8244922433.47661') This results in the invert function not being reliably reversible: (~(~ts1)).normal '1755077566.52338' The cause is that the normal value is based on Timestamp.timestamp which preserves the precision of the value given to the constructor, whereas the invert function uses the limited precision raw value. This patch forces Timestamp.timestamp to have the limited precision value of Timestamp.raw. Change-Id: I4e7fd6078aae3f284628303f20ced66aa702c466 Signed-off-by: Alistair Coles <alistairncoles@gmail.com>
909 lines
41 KiB
Python
909 lines
41 KiB
Python
# 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
|
|
|
|
from 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(0.0, timestamp.Timestamp(0.000001).ceil())
|
|
self.assertEqual(1.0, timestamp.Timestamp(0.000006).ceil())
|
|
self.assertEqual(12345678.0, timestamp.Timestamp(12345678.0).ceil())
|
|
self.assertEqual(12345678.0,
|
|
timestamp.Timestamp(12345678.000001).ceil())
|
|
self.assertEqual(12345679.0,
|
|
timestamp.Timestamp(12345678.000006).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)
|
|
self.assertEqual('1402436408.91203', ts.normal)
|
|
|
|
# 'raw' does not include offset
|
|
ts = timestamp.Timestamp(1402436408.91203, 0xf0)
|
|
self.assertEqual(expected, ts.raw)
|
|
|
|
expected = 175507756652338
|
|
ts = timestamp.Timestamp(1755077566.523385)
|
|
self.assertEqual(expected, ts.raw)
|
|
self.assertEqual('1755077566.52338', ts.normal)
|
|
|
|
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')
|
|
|
|
def test_inversion_reversibility(self):
|
|
ts = timestamp.Timestamp(1755077566.523385)
|
|
inv = ~ts
|
|
inv_inv = ~inv
|
|
self.assertEqual(ts, inv_inv)
|
|
self.assertEqual(ts.normal, inv_inv.normal)
|
|
|
|
inv_inv_inv = ~inv_inv
|
|
self.assertEqual(inv, inv_inv_inv)
|
|
self.assertEqual(inv.normal, inv_inv_inv.normal)
|
|
|
|
ts = timestamp.Timestamp.now()
|
|
inv = ~ts
|
|
inv_inv = ~inv
|
|
self.assertEqual(ts, inv_inv)
|
|
self.assertEqual(ts.normal, inv_inv.normal)
|
|
|
|
|
|
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])
|