Commit 6dec74e7 authored by Kirill Smelkov's avatar Kirill Smelkov

X wcfs: tests: Split tDB into -> tDB + tWCFS

tWCFS is responsible for starting/mounting/unmounting/stopping wcfs
tDB uses tWCFS and provides commit/test service on top.

We'll use tWCFS in the next patch to unmount/stop WCFS processes that
are automatically spawned during test.py
parent bc9eb16f
...@@ -36,12 +36,12 @@ from cpython.exc cimport PyErr_SetFromErrno ...@@ -36,12 +36,12 @@ from cpython.exc cimport PyErr_SetFromErrno
from golang cimport chan, pychan, select, panic, topyexc, cbool from golang cimport chan, pychan, select, panic, topyexc, cbool
from golang cimport sync, time from golang cimport sync, time
# _tDB is pyx part of tDB. # _tWCFS is pyx part of tWCFS.
cdef class _tDB: cdef class _tWCFS:
cdef readonly pychan _closed # chan[structZ] cdef readonly pychan _closed # chan[structZ]
cdef readonly pychan _wcfuseaborted # chan[structZ] cdef readonly pychan _wcfuseaborted # chan[structZ]
def __cinit__(_tDB t): def __cinit__(_tWCFS t):
t._closed = pychan(dtype='C.structZ') t._closed = pychan(dtype='C.structZ')
t._wcfuseaborted = pychan(dtype='C.structZ') t._wcfuseaborted = pychan(dtype='C.structZ')
...@@ -53,7 +53,7 @@ cdef class _tDB: ...@@ -53,7 +53,7 @@ cdef class _tDB:
# but pin handler is failing one way or another - select will wake-up # but pin handler is failing one way or another - select will wake-up
# but, if _abort_ontimeout uses GIL, won't continue to run trying to lock # but, if _abort_ontimeout uses GIL, won't continue to run trying to lock
# GIL -> deadlock. # GIL -> deadlock.
def _abort_ontimeout(_tDB t, double dt, pychan nogilready not None): def _abort_ontimeout(_tWCFS t, double dt, pychan nogilready not None):
cdef chan[double] timeoutch = time.after(dt) cdef chan[double] timeoutch = time.after(dt)
cdef int fdabort = t._wcfuseabort.fileno() cdef int fdabort = t._wcfuseabort.fileno()
emsg1 = "\nC: test timed out after %.1fs\n" % (dt / time.second) emsg1 = "\nC: test timed out after %.1fs\n" % (dt / time.second)
...@@ -63,7 +63,7 @@ cdef class _tDB: ...@@ -63,7 +63,7 @@ cdef class _tDB:
nogilready.chan_structZ().close() nogilready.chan_structZ().close()
t.__abort_ontimeout(dt, timeoutch, fdabort, _emsg1) t.__abort_ontimeout(dt, timeoutch, fdabort, _emsg1)
cdef void __abort_ontimeout(_tDB t, double dt, chan[double] timeoutch, cdef void __abort_ontimeout(_tWCFS t, double dt, chan[double] timeoutch,
int fdabort, const char *emsg1) nogil except +topyexc: int fdabort, const char *emsg1) nogil except +topyexc:
_ = select([ _ = select([
timeoutch.recvs(), # 0 timeoutch.recvs(), # 0
......
...@@ -53,7 +53,7 @@ from zodbtools.util import ashex as h, fromhex ...@@ -53,7 +53,7 @@ from zodbtools.util import ashex as h, fromhex
import pytest; xfail = pytest.mark.xfail import pytest; xfail = pytest.mark.xfail
from pytest import raises, fail from pytest import raises, fail
from wendelin.wcfs.internal import io, mm from wendelin.wcfs.internal import io, mm
from wendelin.wcfs.internal.wcfs_test import _tDB, read_nogil, install_sigbus_trap, fadvise_dontneed from wendelin.wcfs.internal.wcfs_test import _tWCFS, read_nogil, install_sigbus_trap, fadvise_dontneed
from wendelin.wcfs.client._wcfs import _tpywlinkwrite as _twlinkwrite from wendelin.wcfs.client._wcfs import _tpywlinkwrite as _twlinkwrite
...@@ -195,7 +195,7 @@ class DFile: ...@@ -195,7 +195,7 @@ class DFile:
# rev set from outside # rev set from outside
dfile.ddata = {} dfile.ddata = {}
# tDB provides database/wcfs testing environment. # tDB/tWCFS 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 and opened via ZODB connection as .zfile . # The database is initialized with one ZBigFile created and opened via ZODB connection as .zfile .
...@@ -212,16 +212,9 @@ class DFile: ...@@ -212,16 +212,9 @@ class DFile:
# tDB must be explicitly closed once no longer used. # tDB must be explicitly closed once no longer used.
# #
# XXX print -> t.trace/debug() + t.verbose depending on py.test -v -v ? # XXX print -> t.trace/debug() + t.verbose depending on py.test -v -v ?
class tDB(_tDB): class tWCFS(_tWCFS):
@func @func
def __init__(t): def __init__(t):
t.root = testdb.dbopen()
def _(): # close/unlock db if __init__ fails
exc = sys.exc_info()[1]
if exc is not None:
dbclose(t.root)
defer(_)
assert not os.path.exists(testmntpt) assert not os.path.exists(testmntpt)
t.wc = wcfs.join(testzurl, autostart=True) t.wc = wcfs.join(testzurl, autostart=True)
assert t.wc.mountpoint == testmntpt assert t.wc.mountpoint == testmntpt
...@@ -239,6 +232,62 @@ class tDB(_tDB): ...@@ -239,6 +232,62 @@ class tDB(_tDB):
go(t._abort_ontimeout, 10*time.second, nogilready) # NOTE must be: with_timeout << · << wcfs_pin_timeout go(t._abort_ontimeout, 10*time.second, nogilready) # NOTE must be: with_timeout << · << wcfs_pin_timeout
nogilready.recv() # wait till _abort_ontimeout enters nogil nogilready.recv() # wait till _abort_ontimeout enters nogil
# close closes connection to wcfs, unmounts the filesystem and makes sure
# that wcfs server exits.
@func
def close(t):
defer(t._wcfuseabort.close)
defer(t._closed.close)
# unmount and wait for wcfs to exit
def _():
assert not is_mountpoint(t.wc.mountpoint)
os.rmdir(t.wc.mountpoint)
defer(_)
def _():
# kill wcfs.go in case it is deadlocked and does not exit by itself
if procwait_(timeout(), t.wc._proc):
return
eprint("\nC: wcfs.go does not exit")
eprint("-> kill -QUIT wcfs.go ...\n")
os.kill(t.wc._proc.pid, SIGQUIT)
if procwait_(timeout(), t.wc._proc):
return
eprint("\nC: wcfs.go does not exit (after SIGQUIT)")
eprint("-> kill -KILL wcfs.go ...\n")
os.kill(t.wc._proc.pid, SIGKILL)
if procwait_(timeout(), t.wc._proc):
return
eprint("\nC: wcfs.go does not exit (after SIGKILL; probably it is stuck in kernel)")
eprint("-> nothing we can do...\n") # XXX dump /proc/pid/task/*/stack instead (ignore EPERM)
fail("wcfs.go does not exit even after SIGKILL")
defer(_)
def _():
#if not ready(t._wcfuseaborted): XXX kill _wcfuseaborted ?
# assert 0 == subprocess.call(["mountpoint", "-q", t.wc.mountpoint])
assert is_mountpoint(t.wc.mountpoint)
subprocess.check_call(["fusermount", "-u", t.wc.mountpoint])
defer(_)
t.wc.close()
class tDB(tWCFS):
@func
def __init__(t):
t.root = testdb.dbopen()
def _(): # close/unlock db if __init__ fails
exc = sys.exc_info()[1]
if exc is not None:
dbclose(t.root)
defer(_)
# start wcfs after testdb is created
super(tDB, t).__init__()
# ZBigFile(s) scheduled for commit # ZBigFile(s) scheduled for commit
t._changed = {} # ZBigFile -> {} blk -> data t._changed = {} # ZBigFile -> {} blk -> data
...@@ -278,42 +327,9 @@ class tDB(_tDB): ...@@ -278,42 +327,9 @@ class tDB(_tDB):
# it also prints change history to help developer overview current testcase. # it also prints change history to help developer overview current testcase.
@func @func
def close(t): def close(t):
defer(t._wcfuseabort.close) defer(super(tDB, t).close)
defer(t._closed.close)
defer(lambda: dbclose(t.root)) defer(lambda: dbclose(t.root))
# unmount and wait for wcfs to exit
def _():
assert not is_mountpoint(t.wc.mountpoint)
os.rmdir(t.wc.mountpoint)
defer(_)
def _():
# kill wcfs.go in case it is deadlocked and does not exit by itself
if procwait_(timeout(), t.wc._proc):
return
eprint("\nC: wcfs.go does not exit")
eprint("-> kill -QUIT wcfs.go ...\n")
os.kill(t.wc._proc.pid, SIGQUIT)
if procwait_(timeout(), t.wc._proc):
return
eprint("\nC: wcfs.go does not exit (after SIGQUIT)")
eprint("-> kill -KILL wcfs.go ...\n")
os.kill(t.wc._proc.pid, SIGKILL)
if procwait_(timeout(), t.wc._proc):
return
eprint("\nC: wcfs.go does not exit (after SIGKILL; probably it is stuck in kernel)")
eprint("-> nothing we can do...\n") # XXX dump /proc/pid/task/*/stack instead (ignore EPERM)
fail("wcfs.go does not exit even after SIGKILL")
defer(_)
def _():
#if not ready(t._wcfuseaborted): XXX kill _wcfuseaborted ?
# assert 0 == subprocess.call(["mountpoint", "-q", t.wc.mountpoint])
assert is_mountpoint(t.wc.mountpoint)
subprocess.check_call(["fusermount", "-u", t.wc.mountpoint])
defer(_)
defer(t.dump_history) defer(t.dump_history)
for tf in t._files.copy(): for tf in t._files.copy():
tf.close() tf.close()
...@@ -322,7 +338,6 @@ class tDB(_tDB): ...@@ -322,7 +338,6 @@ class tDB(_tDB):
assert len(t._files) == 0 assert len(t._files) == 0
assert len(t._wlinks) == 0 assert len(t._wlinks) == 0
t._wc_zheadfh.close() t._wc_zheadfh.close()
t.wc.close()
# open opens wcfs file corresponding to zf@at and starts to track it. # open opens wcfs file corresponding to zf@at and starts to track it.
# see returned tFile for details. # see returned tFile for details.
......
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