Skip to content

Commit

Permalink
Freezer bugfix: replace datetime instances when leaving freeze context
Browse files Browse the repository at this point in the history
manager.

The labix mocker does not replace classes of instances.
Since we patch the datetime class in the freezer, we need to replace all
instances of patched datetime classes when leaving the freeze context manager.
  • Loading branch information
jone committed Sep 15, 2015
1 parent a332058 commit 54c14da
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs/HISTORY.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Changelog
1.10.3 (unreleased)
-------------------

- Nothing changed yet.
- Freezer bugfix: replace datetime instances when leaving freeze context manager.
[jone]


1.10.2 (2015-07-30)
Expand Down
19 changes: 19 additions & 0 deletions ftw/testing/freezer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from datetime import datetime
from datetime import timedelta
from mocker import expect
from mocker import global_replace
from mocker import Mocker
from mocker import ProxyReplacer
import calendar
import gc


class FreezedClock(object):
Expand Down Expand Up @@ -66,9 +68,26 @@ def now(tz=None):
return self

def __exit__(self, exc_type, exc_value, traceback):
datetime_mock_class = datetime
self.mocker.restore()
self.mocker.verify()

# The labix mocker does only replace pointers to the mock-class which
# are in dicts.
# This does not change the class of instances of our mock-class.
# In order to be able to commit while the time is freezed,
# we need to replace those classes manually.
# Since we cannot patch the class of existing instances,
# we need to construct new instances with the real datetime class and
# replace all mock instances.
for referrer in gc.get_referrers(datetime_mock_class):
if not isinstance(referrer, datetime_mock_class):
continue
if getattr(referrer, '__class__') != datetime_mock_class:
continue
replacement = datetime(*referrer.timetuple()[:6] + (referrer.microsecond,))
global_replace(referrer, replacement)


@contextmanager
def freeze(new_now=None):
Expand Down
16 changes: 16 additions & 0 deletions ftw/testing/tests/test_freezer.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from DateTime import DateTime
from ftw.testing import freeze
from plone.mocktestcase.dummy import Dummy
from unittest2 import TestCase
import datetime
import pytz
import time
import transaction

datetime_module = datetime
datetime_class = datetime.datetime
Expand Down Expand Up @@ -134,3 +136,17 @@ def test_freeze_relative_to_current_time(self):
clock.forward(hours=1)
after = datetime.datetime.now()
self.assertEquals(60 * 60, (after - before).seconds)

def test_patches_are_removed_from_freezed_instances(self):
with freeze():
dummy = Dummy(datetime_class=datetime.datetime,
date=datetime.datetime.now())

self.assertEquals(
{'class': 'datetime.datetime',
'instance': 'datetime.datetime'},

{'class': '.'.join((dummy.datetime_class.__module__,
dummy.datetime_class.__name__)),
'instance': '.'.join((type(dummy.date).__module__,
type(dummy.date).__name__))})

0 comments on commit 54c14da

Please sign in to comment.