Commit 7a4a4937 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 9a3b7413
...@@ -166,6 +166,7 @@ class DFile: ...@@ -166,6 +166,7 @@ class DFile:
# tDB provides database/wcfs testing environment. # tDB provides database/wcfs testing environment.
# #
# Database root and wcfs connection are represented by .root and .wc correspondingly. # Database root and wcfs connection are represented by .root and .wc correspondingly.
# The database is initialized with one ZBigFile created (XXX .zfile)
# #
# The primary way to access wcfs is by opening BigFiles and WatchLinks. # The primary way to access wcfs is by opening BigFiles and WatchLinks.
# A BigFile opened under tDB is represented as tFile - see .open for details. # A BigFile opened under tDB is represented as tFile - see .open for details.
...@@ -205,12 +206,20 @@ class tDB: ...@@ -205,12 +206,20 @@ class tDB:
t._files = set() t._files = set()
t._wlinks = set() t._wlinks = set()
# prepare initail objects for test: zfile, nonzfile
t.root['!file'] = t.nonzfile = Persistent()
t.root['zfile'] = t.zfile = ZBigFile(blksize)
t.at0 = t.commit()
@property @property
def head(t): def head(t):
return t.dFtail[-1].rev return t.dFtail[-1].rev
# close closes test database as well as all tracked files, watch links and wcfs. # close closes test database as well as all tracked files, watch links and wcfs.
# it also prints change history to help developer overview current testcase.
@func
def close(t): def close(t):
defer(t.dump_history)
for tf in t._files.copy(): for tf in t._files.copy():
tf.close() tf.close()
for tw in t._wlinks.copy(): for tw in t._wlinks.copy():
...@@ -1176,38 +1185,33 @@ def iter_revv(t, start=z64, level=0): ...@@ -1176,38 +1185,33 @@ def iter_revv(t, start=z64, level=0):
# ---- actual tests to access data ---- # ---- actual tests to access data ----
# test_wcfs exercises wcfs functionality. # exercise wcfs functionality without wcfs invalidation protocol.
# plain data access + wcfs handling of ZODB invalidations.
@func @func
def test_wcfs(): def test_wcfs_basic():
t = tDB() t = tDB(); zf = t.zfile
defer(t.close) defer(t.close)
defer(t.dump_history)
t.root['!file'] = nonfile = Persistent()
t.root['zfile'] = zf = ZBigFile(blksize)
at0 = t.commit()
# >>> lookup non-BigFile -> must be rejected # >>> lookup non-BigFile -> must be rejected
with raises(OSError) as exc: with raises(OSError) as exc:
t._stat(nonfile) t._stat(t.nonzfile)
assert exc.value.errno == EINVAL assert exc.value.errno == EINVAL
# >>> file initially empty # >>> file initially empty
f = t.open(zf) f = t.open(zf)
f.assertCache([]) f.assertCache([])
f.assertData ([], mtime=at0) f.assertData ([], mtime=t.at0)
# >>> (@at1) commit data -> we can see it on wcfs # >>> (@at1) commit data -> we can see it on wcfs
t.change(zf, {2: 'c1'}) t.change(zf, {2:'c1'})
at1 = t.commit() at1 = t.commit()
f.assertCache([0,0,0]) # initially not cached f.assertCache([0,0,0]) # initially not cached
f.assertData (['','','c1'], mtime=t.head) f.assertData (['','','c1'], mtime=t.head)
# >>> (@at2) commit again -> we can see both latest and snapshotted states # >>> (@at2) commit again -> we can see both latest and snapshotted states
# NOTE blocks d(4) and f(5) will be not yet accessed till "watch+commit" test phase # NOTE blocks d(4) and f(5) will be accessed only in the end
t.change(zf, {2: 'c2', 3: 'd2', 5: 'f2'}) t.change(zf, {2:'c2', 3:'d2', 5:'f2'})
at2 = t.commit() at2 = t.commit()
# f @head # f @head
...@@ -1223,7 +1227,7 @@ def test_wcfs(): ...@@ -1223,7 +1227,7 @@ def test_wcfs():
# >>> (@at3) commit again without changing zf size # >>> (@at3) commit again without changing zf size
f2 = t.open(zf, at=at2) f2 = t.open(zf, at=at2)
t.change(zf, {2: 'c3'}) # FIXME + a3 after δbtree works (hole -> zblk) t.change(zf, {2: 'c3', 5: 'f3'}) # FIXME + a3 after δbtree works (hole -> zblk)
at3 = t.commit() at3 = t.commit()
f.assertCache([1,1,0,1,0,0]) f.assertCache([1,1,0,1,0,0])
...@@ -1239,7 +1243,7 @@ def test_wcfs(): ...@@ -1239,7 +1243,7 @@ def test_wcfs():
f.assertData (['','','c3','d2','x','x'], mtime=t.head) f.assertData (['','','c3','d2','x','x'], mtime=t.head)
# f @at2 # f @at2
# NOTE f2 is accessed but via @at/ not head/ # NOTE f(2) is accessed but via @at/ not head/ ; f(2) in head/zf remains unaccessed
f2.assertCache([0,0,1,0,0,0]) f2.assertCache([0,0,1,0,0,0])
f2.assertData (['','','c2','d2','','f2']) # XXX mtime=at2? f2.assertData (['','','c2','d2','','f2']) # XXX mtime=at2?
...@@ -1256,11 +1260,17 @@ def test_wcfs(): ...@@ -1256,11 +1260,17 @@ def test_wcfs():
if f.cached() != [1,1,1,1,0,0]: if f.cached() != [1,1,1,1,0,0]:
assert sum(f.cached()) > 4*1/2 # > 50% assert sum(f.cached()) > 4*1/2 # > 50%
# >>> XXX commit data to not yet accessed f part - nothing happens # verify all blocks
f.assertData(['','','c3','d2','','f3'])
# """
# >>> invalidation protocol # verify that watch setup is robust to client errors/misbehaviour.
print('\n\n inv. protocol \n\n') @func
def test_wcfs_inv_watch_robust():
t = tDB(); zf = t.zfile
defer(t.close)
t.change(zf, {2:'c1'}
# closeTX/bye cancels blocked pin handlers # closeTX/bye cancels blocked pin handlers
wl = t.openwatch() wl = t.openwatch()
...@@ -1282,7 +1292,7 @@ def test_wcfs(): ...@@ -1282,7 +1292,7 @@ def test_wcfs():
# NOTE if wcfs.go does not fully cleanup this canceled watch and leaves it # NOTE if wcfs.go does not fully cleanup this canceled watch and leaves it
# in half-working state, it will break on further commit, as pin to the # in half-working state, it will break on further commit, as pin to the
# watch won't be handled. # watch won't be handled.
# TODO -> add explicit check for ^^^ if/when moved to separate test. # TODO -> add explicit check for ^^^ if/when moved to separate test. XXX
# invalid requests -> wcfs replies error # invalid requests -> wcfs replies error
wl = t.openwatch() wl = t.openwatch()
...@@ -1308,6 +1318,9 @@ def test_wcfs(): ...@@ -1308,6 +1318,9 @@ def test_wcfs():
" head/at (@%s); …" % (h(zf._p_oid), h(atpast), h(t.head))) " head/at (@%s); …" % (h(zf._p_oid), h(atpast), h(t.head)))
wl.close() wl.close()
def bbb():
# some watch setup/update requests with explicit pinok (also partly # some watch setup/update requests with explicit pinok (also partly
# verifies how tWatchLink.watch computes automatic pinok) # verifies how tWatchLink.watch computes automatic pinok)
wl = t.openwatch() wl = t.openwatch()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment