[Zodb-checkins] SVN: ZODB/trunk/src/ZEO/tests/ Started writing unit
tests for the FileCache class to allow better debugging.
Christian Theune
ct at gocept.com
Mon Dec 10 06:09:06 EST 2007
Log message for revision 82217:
Started writing unit tests for the FileCache class to allow better debugging.
Changed:
A ZODB/trunk/src/ZEO/tests/filecache.txt
U ZODB/trunk/src/ZEO/tests/test_cache.py
-=-
Added: ZODB/trunk/src/ZEO/tests/filecache.txt
===================================================================
--- ZODB/trunk/src/ZEO/tests/filecache.txt (rev 0)
+++ ZODB/trunk/src/ZEO/tests/filecache.txt 2007-12-10 11:09:05 UTC (rev 82217)
@@ -0,0 +1,160 @@
+====================================
+The client cache file implementation
+====================================
+
+This test exercises the FileCache implementation which is responsible for
+maintaining the ZEO client cache on disk. Specifics of persistent cache files
+are not tested.
+
+As the FileCache calls back to the client cache we'll use a dummy to monitor
+those calls:
+
+ >>> class ClientCacheDummy(object):
+ ... def _evicted(self, o):
+ ... pass
+ >>> cache_dummy = ClientCacheDummy()
+
+We'll instanciate a FileCache with 200 bytes of space:
+
+ >>> from ZEO.cache import FileCache
+ >>> fc = FileCache(maxsize=200, fpath=None, parent=cache_dummy)
+
+Initially the cache is empty:
+
+ >>> len(fc)
+ 0
+ >>> list(fc)
+ []
+ >>> fc.getStats()
+ (0, 0, 0, 0, 0)
+
+We'll use a helper function to allow writing OIDs and TIDs as simple integers
+in this test:
+
+ >>> from ZODB.utils import repr_to_oid
+ >>> def oid(o):
+ ... repr = '%016x' % o
+ ... return repr_to_oid(repr)
+ >>> tid = oid
+
+Basic usage
+===========
+
+Objects are represented in the cache using a special `Object` object. Let's
+start with an object of the size 100 bytes:
+
+ >>> from ZEO.cache import Object
+ >>> obj1_1 = Object(key=(oid(1), tid(1)), version='', data='#'*100,
+ ... start_tid=tid(1), end_tid=None)
+
+Notice that the actual object size is a bit larger because of the headers that
+are written for each object:
+
+ >>> obj1_1.size
+ 122
+
+Initially the object is not in the cache:
+
+ >>> (oid(1), tid(1)) in fc
+ False
+
+We can add it to the cache:
+
+ >>> fc.add(obj1_1)
+
+And now it's in the cache:
+
+ >>> (oid(1), tid(1)) in fc
+ True
+ >>> len(fc)
+ 1
+
+We can get it back and the object will be equal but not identical to the one we
+stored:
+
+ >>> obj1_1_copy = fc.access((oid(1), tid(1)))
+ >>> obj1_1_copy.data == obj1_1.data
+ True
+ >>> obj1_1_copy.key == obj1_1.key
+ True
+ >>> obj1_1_copy is obj1_1
+ False
+
+When an object gets superseded we can update it. This only modifies the header,
+not the actual data. This is useful when invalidations tell us about the
+`end_tid` of an object:
+
+ >>> obj1_1.data = '.' * 100
+ >>> obj1_1.end_tid = tid(2)
+ >>> fc.update(obj1_1)
+
+When loading it again we can see that the data was not changed:
+
+ >>> obj1_1_copy = fc.access((oid(1), tid(1)))
+ >>> obj1_1_copy.data # doctest: +ELLIPSIS
+ '#############...################'
+ >>> obj1_1_copy.end_tid
+ '\x00\x00\x00\x00\x00\x00\x00\x02'
+
+Objects can be explicitly removed from the cache:
+
+ >>> fc.remove((oid(1), tid(1)))
+ >>> len(fc)
+ 0
+ >>> (oid(1), tid(1)) in fc
+ False
+
+Evicting objects
+================
+
+When the cached data consumes the whole cache file and more objects need to be
+stored the oldest stored objects are evicted until enough space is available.
+In the next sections we'll exercise some of the special cases of the file
+format and look at the cache after each step.
+
+
+The current state is a cache with two records: the one object which we removed
+from the cache and another free record the reaches to the end of the file.
+
+The first record has a size of 143 bytes:
+
+ 1 ('f') + 4 (size) + 8 (OID) + 8 (TID) + 8 (end_tid) + 2 (version length) + 4 (data length) + 100 (old data) + 8 (OID) == 143
+
+The second record has a size of 45 bytes:
+
+ 1 ('f') + 4 (size) + 40 (free space)
+
+Note that the last byt is an 'x' because the initialisation of the cache file
+forced the absolute size of the file by seeking to byte 200 and writing an 'x'.
+
+ >>> from ZEO.tests.test_cache import hexprint
+ >>> hexprint(fc.f)
+ 00000000 5a 45 43 33 00 00 00 00 00 00 00 00 66 00 00 00 |ZEC3........f...|
+ 00000010 8f 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
+ 00000020 01 00 00 00 00 00 00 00 02 00 00 00 00 00 64 23 |..............d#|
+ 00000030 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 |################|
+ 00000040 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 |################|
+ 00000050 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 |################|
+ 00000060 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 |################|
+ 00000070 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 |################|
+ 00000080 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 |################|
+ 00000090 23 23 23 00 00 00 00 00 00 00 01 66 00 00 00 2d |###........f...-|
+ 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+ 000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+ 000000c0 00 00 00 00 00 00 00 78 |.......x |
+
+Statistic functions
+===================
+
+clearStats
+getStats
+__len__
+__iter__
+__contains__
+__close__
+
+
+Cleanup
+=======
+
+ >>> fc.close()
Property changes on: ZODB/trunk/src/ZEO/tests/filecache.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: ZODB/trunk/src/ZEO/tests/test_cache.py
===================================================================
--- ZODB/trunk/src/ZEO/tests/test_cache.py 2007-12-10 11:04:16 UTC (rev 82216)
+++ ZODB/trunk/src/ZEO/tests/test_cache.py 2007-12-10 11:09:05 UTC (rev 82217)
@@ -16,16 +16,41 @@
import os
import tempfile
import unittest
+import doctest
+import string
import ZEO.cache
from ZODB.utils import p64
+
n1 = p64(1)
n2 = p64(2)
n3 = p64(3)
n4 = p64(4)
n5 = p64(5)
+
+def hexprint(file):
+ file.seek(0)
+ data = file.read()
+ offset = 0
+ while data:
+ line, data = data[:16], data[16:]
+ printable = ""
+ hex = ""
+ for character in line:
+ if character in string.printable:
+ printable += character
+ else:
+ printable += '.'
+ hex += character.encode('hex') + ' '
+ hex = hex[:24] + ' ' + hex[24:]
+ hex = hex.ljust(49)
+ printable = printable.ljust(16)
+ print '%08x %s |%s|' % (offset, hex, printable)
+ offset += 16
+
+
class CacheTests(unittest.TestCase):
def setUp(self):
@@ -152,5 +177,9 @@
eq(copy.current, self.cache.current)
eq(copy.noncurrent, self.cache.noncurrent)
+
def test_suite():
- return unittest.makeSuite(CacheTests)
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(CacheTests))
+ suite.addTest(doctest.DocFileSuite('filecache.txt'))
+ return suite
More information about the Zodb-checkins
mailing list