nti.testing 4.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nti/__init__.py +0 -0
- nti/testing/__init__.py +107 -0
- nti/testing/base.py +574 -0
- nti/testing/layers/__init__.py +53 -0
- nti/testing/layers/cleanup.py +106 -0
- nti/testing/layers/postgres.py +693 -0
- nti/testing/layers/tests/__init__.py +0 -0
- nti/testing/layers/tests/test_postgres.py +17 -0
- nti/testing/layers/zope.py +122 -0
- nti/testing/matchers.py +370 -0
- nti/testing/mock.py +22 -0
- nti/testing/tests/__init__.py +0 -0
- nti/testing/tests/test_base.py +160 -0
- nti/testing/tests/test_component_cleanup_broken.txt +137 -0
- nti/testing/tests/test_layers.py +56 -0
- nti/testing/tests/test_main.py +47 -0
- nti/testing/tests/test_matchers.py +237 -0
- nti/testing/tests/test_time.py +157 -0
- nti/testing/tests/test_zodb.py +326 -0
- nti/testing/time.py +179 -0
- nti/testing/zodb.py +245 -0
- nti.testing-4.2.0.dist-info/LICENSE +11 -0
- nti.testing-4.2.0.dist-info/METADATA +423 -0
- nti.testing-4.2.0.dist-info/RECORD +27 -0
- nti.testing-4.2.0.dist-info/WHEEL +5 -0
- nti.testing-4.2.0.dist-info/top_level.txt +1 -0
- nti.testing-4.2.0.dist-info/zip-safe +1 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Tests for zodb.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import absolute_import
|
|
7
|
+
from __future__ import division
|
|
8
|
+
from __future__ import print_function
|
|
9
|
+
|
|
10
|
+
import io
|
|
11
|
+
import gc
|
|
12
|
+
import unittest
|
|
13
|
+
|
|
14
|
+
import transaction
|
|
15
|
+
from transaction.interfaces import NoTransaction
|
|
16
|
+
from zope import interface
|
|
17
|
+
from nti.testing import zodb
|
|
18
|
+
|
|
19
|
+
# pylint:disable=protected-access,pointless-string-statement
|
|
20
|
+
|
|
21
|
+
NativeStringIO = io.BytesIO if str is bytes else io.StringIO
|
|
22
|
+
|
|
23
|
+
class MockDB(object):
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self.pool = []
|
|
27
|
+
|
|
28
|
+
def open(self):
|
|
29
|
+
conn = MockConn()
|
|
30
|
+
self.pool.append(conn)
|
|
31
|
+
return conn
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MockConn(object):
|
|
35
|
+
closed = False
|
|
36
|
+
minimized = False
|
|
37
|
+
def close(self):
|
|
38
|
+
self.closed = True
|
|
39
|
+
|
|
40
|
+
def cacheMinimize(self):
|
|
41
|
+
self.minimized = True
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class MockDBTrans(zodb.mock_db_trans):
|
|
45
|
+
|
|
46
|
+
def __init__(self, db=None):
|
|
47
|
+
if db is None:
|
|
48
|
+
db = MockDB()
|
|
49
|
+
super().__init__(db)
|
|
50
|
+
self.exc_file = NativeStringIO()
|
|
51
|
+
|
|
52
|
+
class TestMockDBTrans(unittest.TestCase):
|
|
53
|
+
|
|
54
|
+
def setUp(self):
|
|
55
|
+
self._was_explicit = transaction.manager.explicit
|
|
56
|
+
transaction.manager.explicit = False
|
|
57
|
+
|
|
58
|
+
def tearDown(self):
|
|
59
|
+
transaction.manager.explicit = self._was_explicit
|
|
60
|
+
transaction.abort()
|
|
61
|
+
|
|
62
|
+
def test_begins_ends_transaction(self):
|
|
63
|
+
transaction.manager.explicit = True
|
|
64
|
+
with self.assertRaises(NoTransaction):
|
|
65
|
+
transaction.get()
|
|
66
|
+
|
|
67
|
+
with MockDBTrans():
|
|
68
|
+
self.assertIsNotNone(transaction.get())
|
|
69
|
+
|
|
70
|
+
transaction.manager.explicit = True
|
|
71
|
+
with self.assertRaises(NoTransaction):
|
|
72
|
+
transaction.get()
|
|
73
|
+
|
|
74
|
+
def test_sets_txm_explicit(self):
|
|
75
|
+
with MockDBTrans():
|
|
76
|
+
self.assertTrue(transaction.manager.explicit)
|
|
77
|
+
self.assertFalse(transaction.manager.explicit)
|
|
78
|
+
|
|
79
|
+
def test_error_if_mode_changed(self):
|
|
80
|
+
with self.assertRaises(zodb._TransactionManagerModeChanged):
|
|
81
|
+
with MockDBTrans():
|
|
82
|
+
transaction.manager.explicit = False
|
|
83
|
+
|
|
84
|
+
def test_error_if_mode_changed_and_error_in_body(self):
|
|
85
|
+
with self.assertRaises(zodb._TransactionManagerModeChanged) as exc:
|
|
86
|
+
with MockDBTrans():
|
|
87
|
+
transaction.manager.explicit = False
|
|
88
|
+
raise Exception("BodyError") # pylint:disable=broad-exception-raised
|
|
89
|
+
# The backing exception is included
|
|
90
|
+
self.assertIn('BodyError', str(exc.exception))
|
|
91
|
+
|
|
92
|
+
def test_error_if_tx_ended(self):
|
|
93
|
+
with self.assertRaises(zodb._TransactionChanged):
|
|
94
|
+
with MockDBTrans():
|
|
95
|
+
transaction.commit()
|
|
96
|
+
|
|
97
|
+
def test_error_if_tx_changed(self):
|
|
98
|
+
with self.assertRaises(zodb._TransactionChanged):
|
|
99
|
+
with MockDBTrans():
|
|
100
|
+
transaction.commit()
|
|
101
|
+
transaction.begin()
|
|
102
|
+
|
|
103
|
+
def test_error_if_tx_changed_aborts_new(self):
|
|
104
|
+
transaction.manager.explicit = True
|
|
105
|
+
with self.assertRaises(zodb._TransactionChanged):
|
|
106
|
+
with MockDBTrans():
|
|
107
|
+
transaction.commit()
|
|
108
|
+
transaction.begin()
|
|
109
|
+
with self.assertRaises(NoTransaction):
|
|
110
|
+
transaction.get()
|
|
111
|
+
|
|
112
|
+
def test_returns_connection_it_closes(self):
|
|
113
|
+
with MockDBTrans() as conn:
|
|
114
|
+
self.assertIsInstance(conn, MockConn)
|
|
115
|
+
|
|
116
|
+
self.assertTrue(conn.closed)
|
|
117
|
+
self.assertTrue(conn.minimized)
|
|
118
|
+
|
|
119
|
+
def test_aborts_doomed_tx(self):
|
|
120
|
+
aborted = []
|
|
121
|
+
with MockDBTrans():
|
|
122
|
+
transaction.doom()
|
|
123
|
+
tx = transaction.get()
|
|
124
|
+
abort = tx.abort
|
|
125
|
+
|
|
126
|
+
def _abort():
|
|
127
|
+
abort()
|
|
128
|
+
aborted.append(1)
|
|
129
|
+
tx.abort = _abort
|
|
130
|
+
|
|
131
|
+
self.assertEqual(aborted, [1])
|
|
132
|
+
|
|
133
|
+
def test_closing_conn_error_not_shadow_body_error(self):
|
|
134
|
+
class ConnEx(Exception):
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
class BodyEx(Exception):
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
def e():
|
|
141
|
+
raise ConnEx
|
|
142
|
+
|
|
143
|
+
mock_db_trans = MockDBTrans()
|
|
144
|
+
with self.assertRaises(BodyEx):
|
|
145
|
+
with mock_db_trans as conn:
|
|
146
|
+
conn.close = e
|
|
147
|
+
raise BodyEx
|
|
148
|
+
|
|
149
|
+
msg = mock_db_trans.exc_file.getvalue()
|
|
150
|
+
self.assertIn('Unexpected error closing connection', msg)
|
|
151
|
+
self.assertIn('Module nti.testing.zodb', msg)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_aborts_on_abort_error(self):
|
|
155
|
+
aborted = []
|
|
156
|
+
class AbortEx(Exception):
|
|
157
|
+
pass
|
|
158
|
+
with self.assertRaises(AbortEx):
|
|
159
|
+
with MockDBTrans():
|
|
160
|
+
transaction.doom()
|
|
161
|
+
tx = transaction.get()
|
|
162
|
+
abort = tx.abort
|
|
163
|
+
|
|
164
|
+
def _abort():
|
|
165
|
+
abort()
|
|
166
|
+
aborted.append(1)
|
|
167
|
+
del tx.abort
|
|
168
|
+
raise AbortEx
|
|
169
|
+
tx.abort = _abort
|
|
170
|
+
|
|
171
|
+
self.assertEqual(aborted, [1])
|
|
172
|
+
|
|
173
|
+
def test_aborts_on_commit_error(self):
|
|
174
|
+
committed = []
|
|
175
|
+
class CommitEx(Exception):
|
|
176
|
+
pass
|
|
177
|
+
with self.assertRaises(CommitEx):
|
|
178
|
+
with MockDBTrans():
|
|
179
|
+
tx = transaction.get()
|
|
180
|
+
commit = tx.commit
|
|
181
|
+
|
|
182
|
+
def _commit():
|
|
183
|
+
commit()
|
|
184
|
+
committed.append(1)
|
|
185
|
+
del tx.commit
|
|
186
|
+
raise CommitEx
|
|
187
|
+
tx.commit = _commit
|
|
188
|
+
|
|
189
|
+
self.assertEqual(committed, [1])
|
|
190
|
+
|
|
191
|
+
def test_abort_error_does_not_shadow_body_error(self):
|
|
192
|
+
aborted = []
|
|
193
|
+
class AbortEx(Exception):
|
|
194
|
+
pass
|
|
195
|
+
class BodyEx(Exception):
|
|
196
|
+
pass
|
|
197
|
+
mock_db_trans = MockDBTrans()
|
|
198
|
+
with self.assertRaises(BodyEx):
|
|
199
|
+
with mock_db_trans:
|
|
200
|
+
transaction.doom()
|
|
201
|
+
tx = transaction.get()
|
|
202
|
+
abort = tx.abort
|
|
203
|
+
|
|
204
|
+
def _abort():
|
|
205
|
+
abort()
|
|
206
|
+
aborted.append(1)
|
|
207
|
+
del tx.abort
|
|
208
|
+
raise AbortEx
|
|
209
|
+
tx.abort = _abort
|
|
210
|
+
raise BodyEx
|
|
211
|
+
|
|
212
|
+
self.assertEqual(aborted, [1])
|
|
213
|
+
msg = mock_db_trans.exc_file.getvalue()
|
|
214
|
+
self.assertIn('Failed to cleanup trans', msg)
|
|
215
|
+
self.assertIn('Module nti.testing.zodb', msg)
|
|
216
|
+
|
|
217
|
+
def test_error_in_on_opened(self):
|
|
218
|
+
class OpenEx(Exception):
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
class MyFalse(object):
|
|
222
|
+
def __bool__(self):
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
transaction.manager.explicit = MyFalse()
|
|
226
|
+
class MyMock(MockDBTrans):
|
|
227
|
+
seen_tx = None
|
|
228
|
+
aborted = False
|
|
229
|
+
def on_connection_opened(self, conn):
|
|
230
|
+
# pylint:disable=no-member
|
|
231
|
+
self.seen_tx = self._mock_db_trans__current_transaction
|
|
232
|
+
abort = self.seen_tx.abort
|
|
233
|
+
def _abort():
|
|
234
|
+
abort()
|
|
235
|
+
self.aborted = True
|
|
236
|
+
del self.seen_tx.abort
|
|
237
|
+
self.seen_tx.abort = _abort
|
|
238
|
+
raise OpenEx
|
|
239
|
+
|
|
240
|
+
db = MockDB()
|
|
241
|
+
my_mock = MyMock(db)
|
|
242
|
+
with self.assertRaises(OpenEx):
|
|
243
|
+
with my_mock:
|
|
244
|
+
"We never get here"
|
|
245
|
+
|
|
246
|
+
# The txm mode was restored
|
|
247
|
+
self.assertFalse(transaction.manager.explicit)
|
|
248
|
+
self.assertIsInstance(transaction.manager.explicit, MyFalse)
|
|
249
|
+
# the conn was opened, and then closed
|
|
250
|
+
self.assertTrue(db.pool[0].closed)
|
|
251
|
+
self.assertIsNone(my_mock.conn)
|
|
252
|
+
# The transaction that was opened is cleaned up
|
|
253
|
+
self.assertTrue(my_mock.aborted)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class TestZODBLayer(unittest.TestCase):
|
|
257
|
+
|
|
258
|
+
layer = zodb.ZODBLayer
|
|
259
|
+
|
|
260
|
+
def test_registration(self):
|
|
261
|
+
from ZODB.interfaces import IDatabase
|
|
262
|
+
from zope import component
|
|
263
|
+
gsm = component.getGlobalSiteManager()
|
|
264
|
+
reg_db = gsm.getUtility(IDatabase)
|
|
265
|
+
self.assertIsNotNone(reg_db)
|
|
266
|
+
self.assertIs(reg_db, self.layer.db)
|
|
267
|
+
|
|
268
|
+
def test_premature_teardown(self):
|
|
269
|
+
self.layer.tearDown()
|
|
270
|
+
from ZODB.interfaces import IDatabase
|
|
271
|
+
from zope import component
|
|
272
|
+
gsm = component.getGlobalSiteManager()
|
|
273
|
+
reg_db = gsm.queryUtility(IDatabase)
|
|
274
|
+
self.assertIsNone(reg_db)
|
|
275
|
+
|
|
276
|
+
self.layer.setUp()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class IExtra(interface.Interface): # pylint:disable=inherit-non-class
|
|
280
|
+
"""An extra interface."""
|
|
281
|
+
|
|
282
|
+
class IExtra2(interface.Interface): # pylint:disable=inherit-non-class
|
|
283
|
+
"""Another extra interface."""
|
|
284
|
+
|
|
285
|
+
class Object(object):
|
|
286
|
+
|
|
287
|
+
def __init__(self, *args):
|
|
288
|
+
pass
|
|
289
|
+
|
|
290
|
+
class TestResetDbCaches(unittest.TestCase):
|
|
291
|
+
layer = zodb.ZODBLayer
|
|
292
|
+
|
|
293
|
+
def setUp(self):
|
|
294
|
+
super().setUp()
|
|
295
|
+
gc.disable()
|
|
296
|
+
|
|
297
|
+
def tearDown(self):
|
|
298
|
+
gc.enable()
|
|
299
|
+
super().tearDown()
|
|
300
|
+
|
|
301
|
+
def test_persistent_site_closed(self):
|
|
302
|
+
from zope.site.site import LocalSiteManager
|
|
303
|
+
from ZODB import DB
|
|
304
|
+
from ZODB.DemoStorage import DemoStorage
|
|
305
|
+
|
|
306
|
+
interface.classImplements(LocalSiteManager, IExtra)
|
|
307
|
+
|
|
308
|
+
db = DB(DemoStorage())
|
|
309
|
+
transaction.begin()
|
|
310
|
+
conn = db.open()
|
|
311
|
+
site_man = LocalSiteManager(None)
|
|
312
|
+
conn.root()[0] = site_man
|
|
313
|
+
site_man.registerAdapter(Object, [IExtra], IExtra2)
|
|
314
|
+
site_man.getAdapter(LocalSiteManager(None), IExtra2)
|
|
315
|
+
transaction.commit()
|
|
316
|
+
conn.close()
|
|
317
|
+
|
|
318
|
+
zodb.reset_db_caches(db)
|
|
319
|
+
IExtra.changed(None) # pylint:disable=no-value-for-parameter
|
|
320
|
+
|
|
321
|
+
def test_arguments(self):
|
|
322
|
+
# Don't pass the DB, let it get the one from the layer.
|
|
323
|
+
# But do ask for collect, so we get something non-negative on
|
|
324
|
+
# all platforms.
|
|
325
|
+
collected = zodb.reset_db_caches(collect=True)
|
|
326
|
+
self.assertNotEqual(collected, -1)
|
nti/testing/time.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Test support for working with clocks and time.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import absolute_import
|
|
8
|
+
from __future__ import division
|
|
9
|
+
from __future__ import print_function
|
|
10
|
+
|
|
11
|
+
# stdlib imports
|
|
12
|
+
import functools
|
|
13
|
+
from threading import Lock
|
|
14
|
+
import time
|
|
15
|
+
from time import time as _real_time
|
|
16
|
+
from time import gmtime as _real_gmtime
|
|
17
|
+
|
|
18
|
+
import six
|
|
19
|
+
|
|
20
|
+
from nti.testing.mock import Mock
|
|
21
|
+
|
|
22
|
+
__docformat__ = "restructuredtext en"
|
|
23
|
+
|
|
24
|
+
_current_time = _real_time()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class _TimeWrapper(object):
|
|
28
|
+
|
|
29
|
+
def __init__(self, granularity=1.0):
|
|
30
|
+
self._granularity = granularity
|
|
31
|
+
self._lock = Lock()
|
|
32
|
+
self.fake_gmtime = Mock()
|
|
33
|
+
self.fake_time = Mock()
|
|
34
|
+
self._configure_fakes()
|
|
35
|
+
|
|
36
|
+
def _configure_fakes(self):
|
|
37
|
+
def incr():
|
|
38
|
+
global _current_time # pylint:disable=global-statement
|
|
39
|
+
with self._lock:
|
|
40
|
+
_current_time = max(_real_time(), _current_time + self._granularity)
|
|
41
|
+
return _current_time
|
|
42
|
+
self.fake_time.side_effect = incr
|
|
43
|
+
|
|
44
|
+
def incr_gmtime(*seconds):
|
|
45
|
+
if seconds:
|
|
46
|
+
assert len(seconds) == 1
|
|
47
|
+
now = seconds[0]
|
|
48
|
+
else:
|
|
49
|
+
now = incr()
|
|
50
|
+
return _real_gmtime(now)
|
|
51
|
+
self.fake_gmtime.side_effect = incr_gmtime
|
|
52
|
+
|
|
53
|
+
def install_fakes(self):
|
|
54
|
+
time.time = self.fake_time
|
|
55
|
+
time.gmtime = self.fake_gmtime
|
|
56
|
+
|
|
57
|
+
__enter__ = install_fakes
|
|
58
|
+
|
|
59
|
+
def close(self, *_args):
|
|
60
|
+
time.time = _real_time
|
|
61
|
+
time.gmtime = _real_gmtime
|
|
62
|
+
|
|
63
|
+
__exit__ = close
|
|
64
|
+
|
|
65
|
+
def __call__(self, func):
|
|
66
|
+
@functools.wraps(func)
|
|
67
|
+
def wrapper(*args, **kwargs):
|
|
68
|
+
with self:
|
|
69
|
+
return func(*args, **kwargs)
|
|
70
|
+
return wrapper
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def time_monotonically_increases(func_or_granularity):
|
|
75
|
+
"""
|
|
76
|
+
Decorate a unittest method with this function to cause the value
|
|
77
|
+
of :func:`time.time` (and :func:`time.gmtime`) to monotonically
|
|
78
|
+
increase by one each time it is called. This ensures things like
|
|
79
|
+
last modified dates always increase.
|
|
80
|
+
|
|
81
|
+
We make three guarantees about the value of :func:`time.time`
|
|
82
|
+
returned while the decorated function is running:
|
|
83
|
+
|
|
84
|
+
1. It is always *at least* the value of the *real*
|
|
85
|
+
:func:`time.time`;
|
|
86
|
+
|
|
87
|
+
2. Each call returns a value greater than the previous call;
|
|
88
|
+
|
|
89
|
+
3. Those two constraints hold across different invocations of
|
|
90
|
+
functions decorated.
|
|
91
|
+
|
|
92
|
+
This decorator can be applied to a method in a test case::
|
|
93
|
+
|
|
94
|
+
class TestThing(unittest.TestCase)
|
|
95
|
+
@time_monotonically_increases
|
|
96
|
+
def test_method(self):
|
|
97
|
+
t = time.time()
|
|
98
|
+
...
|
|
99
|
+
|
|
100
|
+
It can also be applied to a bare function taking any number of
|
|
101
|
+
arguments::
|
|
102
|
+
|
|
103
|
+
@time_monotonically_increases
|
|
104
|
+
def utility_function(a, b, c=1):
|
|
105
|
+
t = time.time()
|
|
106
|
+
...
|
|
107
|
+
|
|
108
|
+
By default, the time will be incremented in 1.0 second intervals.
|
|
109
|
+
You can specify a particular granularity as an argument; this is
|
|
110
|
+
useful to keep from running too far ahead of the real clock::
|
|
111
|
+
|
|
112
|
+
@time_monotonically_increases(0.1)
|
|
113
|
+
def smaller_increment():
|
|
114
|
+
t1 = time.time()
|
|
115
|
+
t2 = time.time()
|
|
116
|
+
assrt t2 == t1 + 0.1
|
|
117
|
+
|
|
118
|
+
.. seealso:: `nti.testing.time.reset_monotonic_time`
|
|
119
|
+
|
|
120
|
+
.. versionchanged:: 2.1
|
|
121
|
+
Add support for ``time.gmtime``.
|
|
122
|
+
.. versionchanged:: 2.1
|
|
123
|
+
Now thread safe.
|
|
124
|
+
.. versionchanged:: 2.0
|
|
125
|
+
The decorated function returns whatever the passed function returns.
|
|
126
|
+
.. versionchanged:: 2.0
|
|
127
|
+
Properly handle more than one argument to the underlying function.
|
|
128
|
+
.. versionchanged:: 2.0
|
|
129
|
+
Ensure that the values returned are at least the real time. Previously
|
|
130
|
+
they were integers starting at 0.
|
|
131
|
+
.. versionchanged:: 2.0
|
|
132
|
+
Ensure that the values returned are increasing even across different
|
|
133
|
+
invocations of a decorated function or different decorated functions. Previously
|
|
134
|
+
different functions would return the same sequence.
|
|
135
|
+
.. versionchanged:: 2.0
|
|
136
|
+
Allow specifying the granularity of clock increments. Keep at
|
|
137
|
+
1.0 seconds for backwards compatibility by default.
|
|
138
|
+
"""
|
|
139
|
+
if isinstance(func_or_granularity, (six.integer_types, float)):
|
|
140
|
+
# We're being used as a factory.
|
|
141
|
+
wrapper_factory = _TimeWrapper(func_or_granularity)
|
|
142
|
+
return wrapper_factory
|
|
143
|
+
|
|
144
|
+
# We're being used bare
|
|
145
|
+
wrapper_factory = _TimeWrapper()
|
|
146
|
+
return wrapper_factory(func_or_granularity)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def reset_monotonic_time(value=0.0):
|
|
150
|
+
"""
|
|
151
|
+
Make the monotonic clock return the real time on its next
|
|
152
|
+
call.
|
|
153
|
+
|
|
154
|
+
.. versionadded:: 2.0
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
global _current_time # pylint:disable=global-statement
|
|
158
|
+
_current_time = value
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class MonotonicallyIncreasingTimeLayerMixin(object):
|
|
162
|
+
"""
|
|
163
|
+
A helper for layers that need time to increase monotonically.
|
|
164
|
+
|
|
165
|
+
You can either mix this in to a layer object, or instantiate it
|
|
166
|
+
and call the methods directly.
|
|
167
|
+
|
|
168
|
+
.. versionadded:: 2.2
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def __init__(self, granularity=1.0):
|
|
172
|
+
self.time_manager = _TimeWrapper(granularity)
|
|
173
|
+
|
|
174
|
+
def testSetUp(self):
|
|
175
|
+
self.time_manager.install_fakes()
|
|
176
|
+
|
|
177
|
+
def testTearDown(self):
|
|
178
|
+
self.time_manager.close()
|
|
179
|
+
reset_monotonic_time()
|