2016-03-30 06:30:57 +08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
import subprocess
|
|
|
|
import pytest
|
|
|
|
import os
|
2017-01-11 06:34:42 +08:00
|
|
|
import stat
|
2016-03-30 06:30:57 +08:00
|
|
|
import time
|
2016-10-14 00:07:55 +08:00
|
|
|
from os.path import join as pjoin
|
|
|
|
|
|
|
|
basename = pjoin(os.path.dirname(__file__), '..')
|
2016-03-30 06:30:57 +08:00
|
|
|
|
2016-10-10 12:46:39 +08:00
|
|
|
def wait_for_mount(mount_process, mnt_dir,
|
|
|
|
test_fn=os.path.ismount):
|
2016-03-30 06:30:57 +08:00
|
|
|
elapsed = 0
|
|
|
|
while elapsed < 30:
|
2016-10-10 12:46:39 +08:00
|
|
|
if test_fn(mnt_dir):
|
2016-03-30 06:30:57 +08:00
|
|
|
return True
|
|
|
|
if mount_process.poll() is not None:
|
|
|
|
pytest.fail('file system process terminated prematurely')
|
|
|
|
time.sleep(0.1)
|
|
|
|
elapsed += 0.1
|
|
|
|
pytest.fail("mountpoint failed to come up")
|
|
|
|
|
|
|
|
def cleanup(mnt_dir):
|
2016-10-14 00:07:55 +08:00
|
|
|
# Don't bother trying Valgrind if things already went wrong
|
|
|
|
|
2016-10-26 12:06:14 +08:00
|
|
|
subprocess.call([pjoin(basename, 'util', 'fusermount3'),
|
2016-10-14 00:07:55 +08:00
|
|
|
'-z', '-u', mnt_dir],
|
2016-03-30 06:30:57 +08:00
|
|
|
stdout=subprocess.DEVNULL,
|
|
|
|
stderr=subprocess.STDOUT)
|
|
|
|
|
|
|
|
def umount(mount_process, mnt_dir):
|
2016-11-29 13:33:14 +08:00
|
|
|
# fusermount3 will be setuid root, so we can only trace it with
|
2016-10-14 00:07:55 +08:00
|
|
|
# valgrind if we're root
|
|
|
|
if os.getuid() == 0:
|
|
|
|
cmdline = base_cmdline
|
|
|
|
else:
|
|
|
|
cmdline = []
|
|
|
|
|
2016-10-26 12:06:14 +08:00
|
|
|
cmdline = cmdline + [ pjoin(basename, 'util', 'fusermount3'),
|
2016-10-14 00:07:55 +08:00
|
|
|
'-z', '-u', mnt_dir ]
|
|
|
|
subprocess.check_call(cmdline)
|
2016-03-30 06:30:57 +08:00
|
|
|
assert not os.path.ismount(mnt_dir)
|
|
|
|
|
|
|
|
# Give mount process a little while to terminate. Popen.wait(timeout)
|
|
|
|
# was only added in 3.3...
|
|
|
|
elapsed = 0
|
|
|
|
while elapsed < 30:
|
|
|
|
code = mount_process.poll()
|
|
|
|
if code is not None:
|
|
|
|
if code == 0:
|
|
|
|
return
|
|
|
|
pytest.fail('file system process terminated with code %s' % (code,))
|
|
|
|
time.sleep(0.1)
|
|
|
|
elapsed += 0.1
|
|
|
|
pytest.fail('mount process did not terminate')
|
2016-10-03 13:31:13 +08:00
|
|
|
|
|
|
|
|
2016-10-09 10:26:32 +08:00
|
|
|
def safe_sleep(secs):
|
|
|
|
'''Like time.sleep(), but sleep for at least *secs*
|
|
|
|
|
|
|
|
`time.sleep` may sleep less than the given period if a signal is
|
|
|
|
received. This function ensures that we sleep for at least the
|
|
|
|
desired time.
|
|
|
|
'''
|
|
|
|
|
|
|
|
now = time.time()
|
|
|
|
end = now + secs
|
|
|
|
while now < end:
|
|
|
|
time.sleep(end - now)
|
|
|
|
now = time.time()
|
|
|
|
|
2017-01-11 06:34:42 +08:00
|
|
|
def fuse_test_marker():
|
|
|
|
'''Return a pytest.marker that indicates FUSE availability
|
|
|
|
|
|
|
|
If system/user/environment does not support FUSE, return
|
|
|
|
a `pytest.mark.skip` object with more details. If FUSE is
|
|
|
|
supported, return `pytest.mark.uses_fuse()`.
|
|
|
|
'''
|
|
|
|
|
|
|
|
skip = lambda x: pytest.mark.skip(reason=x)
|
|
|
|
|
|
|
|
with subprocess.Popen(['which', 'fusermount3'], stdout=subprocess.PIPE,
|
|
|
|
universal_newlines=True) as which:
|
|
|
|
fusermount_path = which.communicate()[0].strip()
|
|
|
|
|
|
|
|
if not fusermount_path or which.returncode != 0:
|
|
|
|
return skip("Can't find fusermount executable")
|
|
|
|
|
|
|
|
if not os.path.exists('/dev/fuse'):
|
|
|
|
return skip("FUSE kernel module does not seem to be loaded")
|
|
|
|
|
|
|
|
if os.getuid() == 0:
|
|
|
|
return pytest.mark.uses_fuse()
|
|
|
|
|
|
|
|
mode = os.stat(fusermount_path).st_mode
|
|
|
|
if mode & stat.S_ISUID == 0:
|
|
|
|
return skip('fusermount executable not setuid, and we are not root.')
|
|
|
|
|
|
|
|
try:
|
|
|
|
fd = os.open('/dev/fuse', os.O_RDWR)
|
|
|
|
except OSError as exc:
|
|
|
|
return skip('Unable to open /dev/fuse: %s' % exc.strerror)
|
|
|
|
else:
|
|
|
|
os.close(fd)
|
|
|
|
|
|
|
|
return pytest.mark.uses_fuse()
|
|
|
|
|
2017-01-06 01:37:00 +08:00
|
|
|
# If valgrind is available, use it
|
2016-10-03 13:31:13 +08:00
|
|
|
def has_program(name):
|
2016-10-04 00:08:24 +08:00
|
|
|
try:
|
|
|
|
ret = subprocess.call([name, '--version'],
|
|
|
|
stdout=subprocess.DEVNULL,
|
|
|
|
stderr=subprocess.DEVNULL)
|
|
|
|
except FileNotFoundError:
|
|
|
|
return False
|
|
|
|
return ret == 0
|
2016-10-03 13:31:13 +08:00
|
|
|
|
2017-01-06 01:37:00 +08:00
|
|
|
if has_program('valgrind'):
|
|
|
|
base_cmdline = [ 'valgrind', '-q', '--' ]
|
2016-10-03 13:31:13 +08:00
|
|
|
else:
|
|
|
|
base_cmdline = []
|
2016-10-14 00:07:55 +08:00
|
|
|
|
|
|
|
|
2016-11-29 13:33:14 +08:00
|
|
|
# Try to use local fusermount3
|
2016-10-14 00:07:55 +08:00
|
|
|
os.environ['PATH'] = '%s:%s' % (pjoin(basename, 'util'), os.environ['PATH'])
|