"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.2.3/test/test_examples.py" (11 May 2018, 19773 Bytes) of package /linux/misc/fuse-3.2.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "test_examples.py": 3.1.1_vs_3.2.0.

    1 #!/usr/bin/env python3
    2 
    3 if __name__ == '__main__':
    4     import pytest
    5     import sys
    6     sys.exit(pytest.main([__file__] + sys.argv[1:]))
    7 
    8 import subprocess
    9 import os
   10 import sys
   11 import pytest
   12 import stat
   13 import shutil
   14 import filecmp
   15 import time
   16 import errno
   17 import sys
   18 from tempfile import NamedTemporaryFile
   19 from contextlib import contextmanager
   20 from util import (wait_for_mount, umount, cleanup, base_cmdline,
   21                   safe_sleep, basename, fuse_test_marker, test_printcap,
   22                   fuse_proto)
   23 from os.path import join as pjoin
   24 
   25 pytestmark = fuse_test_marker()
   26 
   27 TEST_FILE = __file__
   28 
   29 with open(TEST_FILE, 'rb') as fh:
   30     TEST_DATA = fh.read()
   31 
   32 def name_generator(__ctr=[0]):
   33     __ctr[0] += 1
   34     return 'testfile_%d' % __ctr[0]
   35 
   36 options = [ [] ]
   37 if sys.platform == 'linux':
   38     options.append(['-o', 'clone_fd'])
   39 @pytest.mark.parametrize("options", options)
   40 @pytest.mark.parametrize("name", ('hello', 'hello_ll'))
   41 def test_hello(tmpdir, name, options):
   42     mnt_dir = str(tmpdir)
   43     cmdline = base_cmdline + \
   44               [ pjoin(basename, 'example', name),
   45                 '-f', mnt_dir ] + options
   46     if name == 'hello_ll':
   47         # supports single-threading only
   48         cmdline.append('-s')
   49     mount_process = subprocess.Popen(cmdline)
   50     try:
   51         wait_for_mount(mount_process, mnt_dir)
   52         assert os.listdir(mnt_dir) == [ 'hello' ]
   53         filename = pjoin(mnt_dir, 'hello')
   54         with open(filename, 'r') as fh:
   55             assert fh.read() == 'Hello World!\n'
   56         with pytest.raises(IOError) as exc_info:
   57             open(filename, 'r+')
   58         assert exc_info.value.errno == errno.EACCES
   59         with pytest.raises(IOError) as exc_info:
   60             open(filename + 'does-not-exist', 'r+')
   61         assert exc_info.value.errno == errno.ENOENT
   62     except:
   63         cleanup(mnt_dir)
   64         raise
   65     else:
   66         umount(mount_process, mnt_dir)
   67 
   68 
   69 @pytest.mark.parametrize("writeback", (False, True))
   70 @pytest.mark.parametrize("debug", (False, True))
   71 def test_passthrough_ll(tmpdir, writeback, debug, capfd):
   72     
   73     progname = pjoin(basename, 'example', 'passthrough_ll')
   74     if not os.path.exists(progname):
   75         pytest.skip('%s not built' % os.path.basename(progname))
   76     
   77     # Avoid false positives from libfuse debug messages
   78     if debug:
   79         capfd.register_output(r'^   unique: [0-9]+, error: -[0-9]+ .+$',
   80                               count=0)
   81 
   82     mnt_dir = str(tmpdir.mkdir('mnt'))
   83     src_dir = str(tmpdir.mkdir('src'))
   84 
   85     cmdline = base_cmdline + [ progname, '-f', mnt_dir ]
   86     if debug:
   87         cmdline.append('-d')
   88 
   89     if writeback:
   90         cmdline.append('-o')
   91         cmdline.append('writeback')
   92         
   93     mount_process = subprocess.Popen(cmdline)
   94     try:
   95         wait_for_mount(mount_process, mnt_dir)
   96         work_dir = mnt_dir + src_dir
   97 
   98         tst_statvfs(work_dir)
   99         tst_readdir(src_dir, work_dir)
  100         tst_open_read(src_dir, work_dir)
  101         tst_open_write(src_dir, work_dir)
  102         tst_create(work_dir)
  103         tst_passthrough(src_dir, work_dir)
  104         tst_append(src_dir, work_dir)
  105         tst_seek(src_dir, work_dir)
  106     except:
  107         cleanup(mnt_dir)
  108         raise
  109     else:
  110         umount(mount_process, mnt_dir)
  111 
  112 @pytest.mark.parametrize("name", ('passthrough', 'passthrough_fh'))
  113 @pytest.mark.parametrize("debug", (False, True))
  114 def test_passthrough(tmpdir, name, debug, capfd):
  115     
  116     # Avoid false positives from libfuse debug messages
  117     if debug:
  118         capfd.register_output(r'^   unique: [0-9]+, error: -[0-9]+ .+$',
  119                               count=0)
  120 
  121     # test_syscalls prints "No error" under FreeBSD
  122     capfd.register_output(r"^ \d\d \[[^\]]+ message: 'No error: 0'\]",
  123                           count=0)
  124 
  125     mnt_dir = str(tmpdir.mkdir('mnt'))
  126     src_dir = str(tmpdir.mkdir('src'))
  127 
  128     cmdline = base_cmdline + \
  129               [ pjoin(basename, 'example', name),
  130                 '-f', mnt_dir ]
  131     if debug:
  132         cmdline.append('-d')
  133     mount_process = subprocess.Popen(cmdline)
  134     try:
  135         wait_for_mount(mount_process, mnt_dir)
  136         work_dir = mnt_dir + src_dir
  137 
  138         tst_statvfs(work_dir)
  139         tst_readdir(src_dir, work_dir)
  140         tst_open_read(src_dir, work_dir)
  141         tst_open_write(src_dir, work_dir)
  142         tst_create(work_dir)
  143         tst_passthrough(src_dir, work_dir)
  144         tst_append(src_dir, work_dir)
  145         tst_seek(src_dir, work_dir)
  146         tst_mkdir(work_dir)
  147         tst_rmdir(src_dir, work_dir)
  148         tst_unlink(src_dir, work_dir)
  149         tst_symlink(work_dir)
  150         if os.getuid() == 0:
  151             tst_chown(work_dir)
  152 
  153         # Underlying fs may not have full nanosecond resolution
  154         tst_utimens(work_dir, ns_tol=1000)
  155 
  156         tst_link(work_dir)
  157         tst_truncate_path(work_dir)
  158         tst_truncate_fd(work_dir)
  159         tst_open_unlink(work_dir)
  160         
  161         subprocess.check_call([ os.path.join(basename, 'test', 'test_syscalls'),
  162                                 work_dir, ':' + src_dir ])
  163     except:
  164         cleanup(mnt_dir)
  165         raise
  166     else:
  167         umount(mount_process, mnt_dir)
  168 
  169 @pytest.mark.skipif(fuse_proto < (7,11),
  170                     reason='not supported by running kernel')
  171 def test_ioctl(tmpdir):
  172     progname = pjoin(basename, 'example', 'ioctl')
  173     if not os.path.exists(progname):
  174         pytest.skip('%s not built' % os.path.basename(progname))
  175     
  176     mnt_dir = str(tmpdir)
  177     testfile = pjoin(mnt_dir, 'fioc')
  178     cmdline = base_cmdline + [progname, '-f', mnt_dir ]
  179     mount_process = subprocess.Popen(cmdline)
  180     try:
  181         wait_for_mount(mount_process, mnt_dir)
  182 
  183         cmdline = base_cmdline + \
  184                   [ pjoin(basename, 'example', 'ioctl_client'),
  185                     testfile ]
  186         assert subprocess.check_output(cmdline) == b'0\n'
  187         with open(testfile, 'wb') as fh:
  188             fh.write(b'foobar')
  189         assert subprocess.check_output(cmdline) == b'6\n'
  190         subprocess.check_call(cmdline + [ '3' ])
  191         with open(testfile, 'rb') as fh:
  192             assert fh.read()== b'foo'
  193     except:
  194         cleanup(mnt_dir)
  195         raise
  196     else:
  197         umount(mount_process, mnt_dir)
  198 
  199 def test_poll(tmpdir):
  200     mnt_dir = str(tmpdir)
  201     cmdline = base_cmdline + [pjoin(basename, 'example', 'poll'),
  202                '-f', mnt_dir ]
  203     mount_process = subprocess.Popen(cmdline)
  204     try:
  205         wait_for_mount(mount_process, mnt_dir)
  206         cmdline = base_cmdline + \
  207                   [ pjoin(basename, 'example', 'poll_client') ]
  208         subprocess.check_call(cmdline, cwd=mnt_dir)
  209     except:
  210         cleanup(mnt_dir)
  211         raise
  212     else:
  213         umount(mount_process, mnt_dir)
  214 
  215 def test_null(tmpdir):
  216     progname = pjoin(basename, 'example', 'null')
  217     if not os.path.exists(progname):
  218         pytest.skip('%s not built' % os.path.basename(progname))
  219     
  220     mnt_file = str(tmpdir) + '/file'
  221     with open(mnt_file, 'w') as fh:
  222         fh.write('dummy')
  223     cmdline = base_cmdline + [ progname, '-f', mnt_file ]
  224     mount_process = subprocess.Popen(cmdline)
  225     def test_fn(name):
  226         return os.stat(name).st_size > 4000
  227     try:
  228         wait_for_mount(mount_process, mnt_file, test_fn)
  229         with open(mnt_file, 'rb') as fh:
  230             assert fh.read(382) == b'\0' * 382
  231         with open(mnt_file, 'wb') as fh:
  232             fh.write(b'whatever')
  233     except:
  234         cleanup(mnt_file)
  235         raise
  236     else:
  237         umount(mount_process, mnt_file)
  238 
  239 
  240 @pytest.mark.skipif(fuse_proto < (7,12),
  241                     reason='not supported by running kernel')
  242 @pytest.mark.parametrize("notify", (True, False))
  243 def test_notify_inval_entry(tmpdir, notify):
  244     mnt_dir = str(tmpdir)
  245     cmdline = base_cmdline + \
  246               [ pjoin(basename, 'example', 'notify_inval_entry'),
  247                 '-f', '--update-interval=1',
  248                 '--timeout=5', mnt_dir ]
  249     if not notify:
  250         cmdline.append('--no-notify')
  251     mount_process = subprocess.Popen(cmdline)
  252     try:
  253         wait_for_mount(mount_process, mnt_dir)
  254         fname = pjoin(mnt_dir, os.listdir(mnt_dir)[0])
  255         try:
  256             os.stat(fname)
  257         except FileNotFoundError:
  258             # We may have hit a race condition and issued
  259             # readdir just before the name changed
  260             fname = pjoin(mnt_dir, os.listdir(mnt_dir)[0])
  261             os.stat(fname)
  262 
  263         safe_sleep(2)
  264         if not notify:
  265             os.stat(fname)
  266             safe_sleep(5)
  267         with pytest.raises(FileNotFoundError):
  268             os.stat(fname)
  269     except:
  270         cleanup(mnt_dir)
  271         raise
  272     else:
  273         umount(mount_process, mnt_dir)
  274 
  275 @pytest.mark.skipif(os.getuid() != 0,
  276                     reason='needs to run as root')
  277 def test_cuse(capfd):
  278 
  279     # Valgrind warns about unknown ioctls, that's ok
  280     capfd.register_output(r'^==([0-9]+).+unhandled ioctl.+\n'
  281                           r'==\1== \s{3}.+\n'
  282                           r'==\1== \s{3}.+$', count=0)
  283 
  284     devname = 'cuse-test-%d' % os.getpid()
  285     devpath = '/dev/%s' % devname
  286     cmdline = base_cmdline + \
  287               [ pjoin(basename, 'example', 'cuse'),
  288                 '-f', '--name=%s' % devname ]
  289     mount_process = subprocess.Popen(cmdline)
  290 
  291     cmdline = base_cmdline + \
  292               [ pjoin(basename, 'example', 'cuse_client'),
  293                 devpath ]
  294     try:
  295         wait_for_mount(mount_process, devpath,
  296                        test_fn=os.path.exists)
  297         assert subprocess.check_output(cmdline + ['s']) == b'0\n'
  298         data = b'some test data'
  299         off = 5
  300         proc = subprocess.Popen(cmdline + [ 'w', str(len(data)), str(off) ],
  301                                 stdin=subprocess.PIPE)
  302         proc.stdin.write(data)
  303         proc.stdin.close()
  304         assert proc.wait(timeout=10) == 0
  305         size = str(off + len(data)).encode() + b'\n'
  306         assert subprocess.check_output(cmdline + ['s']) == size
  307         out = subprocess.check_output(
  308             cmdline + [ 'r', str(off + len(data) + 2), '0' ])
  309         assert out == (b'\0' * off) + data
  310     finally:
  311         mount_process.terminate()
  312 
  313 @contextmanager
  314 def os_open(name, flags):
  315     fd = os.open(name, flags)
  316     try:
  317         yield fd
  318     finally:
  319         os.close(fd)
  320 
  321 def os_create(name):
  322     os.close(os.open(name, os.O_CREAT | os.O_RDWR))
  323 
  324 def tst_unlink(src_dir, mnt_dir):
  325     name = name_generator()
  326     fullname = mnt_dir + "/" + name
  327     with open(pjoin(src_dir, name), 'wb') as fh:
  328         fh.write(b'hello')
  329     assert name in os.listdir(mnt_dir)
  330     os.unlink(fullname)
  331     with pytest.raises(OSError) as exc_info:
  332         os.stat(fullname)
  333     assert exc_info.value.errno == errno.ENOENT
  334     assert name not in os.listdir(mnt_dir)
  335 
  336 def tst_mkdir(mnt_dir):
  337     dirname = name_generator()
  338     fullname = mnt_dir + "/" + dirname
  339     os.mkdir(fullname)
  340     fstat = os.stat(fullname)
  341     assert stat.S_ISDIR(fstat.st_mode)
  342     assert os.listdir(fullname) ==  []
  343     # Some filesystem (e.g. BTRFS) don't track st_nlink for directories
  344     assert fstat.st_nlink in (1,2)
  345     assert dirname in os.listdir(mnt_dir)
  346 
  347 def tst_rmdir(src_dir, mnt_dir):
  348     name = name_generator()
  349     fullname = mnt_dir + "/" + name
  350     os.mkdir(pjoin(src_dir, name))
  351     assert name in os.listdir(mnt_dir)
  352     os.rmdir(fullname)
  353     with pytest.raises(OSError) as exc_info:
  354         os.stat(fullname)
  355     assert exc_info.value.errno == errno.ENOENT
  356     assert name not in os.listdir(mnt_dir)
  357 
  358 def tst_symlink(mnt_dir):
  359     linkname = name_generator()
  360     fullname = mnt_dir + "/" + linkname
  361     os.symlink("/imaginary/dest", fullname)
  362     fstat = os.lstat(fullname)
  363     assert stat.S_ISLNK(fstat.st_mode)
  364     assert os.readlink(fullname) == "/imaginary/dest"
  365     assert fstat.st_nlink == 1
  366     assert linkname in os.listdir(mnt_dir)
  367 
  368 def tst_create(mnt_dir):
  369     name = name_generator()
  370     fullname = pjoin(mnt_dir, name)
  371     with pytest.raises(OSError) as exc_info:
  372         os.stat(fullname)
  373     assert exc_info.value.errno == errno.ENOENT
  374     assert name not in os.listdir(mnt_dir)
  375 
  376     fd = os.open(fullname, os.O_CREAT | os.O_RDWR)
  377     os.close(fd)
  378 
  379     assert name in os.listdir(mnt_dir)
  380     fstat = os.lstat(fullname)
  381     assert stat.S_ISREG(fstat.st_mode)
  382     assert fstat.st_nlink == 1
  383     assert fstat.st_size == 0
  384 
  385 def tst_chown(mnt_dir):
  386     filename = pjoin(mnt_dir, name_generator())
  387     os.mkdir(filename)
  388     fstat = os.lstat(filename)
  389     uid = fstat.st_uid
  390     gid = fstat.st_gid
  391 
  392     uid_new = uid + 1
  393     os.chown(filename, uid_new, -1)
  394     fstat = os.lstat(filename)
  395     assert fstat.st_uid == uid_new
  396     assert fstat.st_gid == gid
  397 
  398     gid_new = gid + 1
  399     os.chown(filename, -1, gid_new)
  400     fstat = os.lstat(filename)
  401     assert fstat.st_uid == uid_new
  402     assert fstat.st_gid == gid_new
  403 
  404 def tst_open_read(src_dir, mnt_dir):
  405     name = name_generator()
  406     with open(pjoin(src_dir, name), 'wb') as fh_out, \
  407          open(TEST_FILE, 'rb') as fh_in:
  408         shutil.copyfileobj(fh_in, fh_out)
  409 
  410     assert filecmp.cmp(pjoin(mnt_dir, name), TEST_FILE, False)
  411 
  412 def tst_open_write(src_dir, mnt_dir):
  413     name = name_generator()
  414     os_create(pjoin(src_dir, name))
  415     fullname = pjoin(mnt_dir, name)
  416     with open(fullname, 'wb') as fh_out, \
  417          open(TEST_FILE, 'rb') as fh_in:
  418         shutil.copyfileobj(fh_in, fh_out)
  419 
  420     assert filecmp.cmp(fullname, TEST_FILE, False)
  421 
  422 def tst_append(src_dir, mnt_dir):
  423     name = name_generator()
  424     os_create(pjoin(src_dir, name))
  425     fullname = pjoin(mnt_dir, name)
  426     with os_open(fullname, os.O_WRONLY) as fd:
  427         os.write(fd, b'foo\n')
  428     with os_open(fullname, os.O_WRONLY|os.O_APPEND) as fd:
  429         os.write(fd, b'bar\n')
  430 
  431     with open(fullname, 'rb') as fh:
  432         assert fh.read() == b'foo\nbar\n'
  433 
  434 def tst_seek(src_dir, mnt_dir):
  435     name = name_generator()
  436     os_create(pjoin(src_dir, name))
  437     fullname = pjoin(mnt_dir, name)
  438     with os_open(fullname, os.O_WRONLY) as fd:
  439         os.lseek(fd, 1, os.SEEK_SET)
  440         os.write(fd, b'foobar\n')
  441     with os_open(fullname, os.O_WRONLY) as fd:
  442         os.lseek(fd, 4, os.SEEK_SET)
  443         os.write(fd, b'com')
  444         
  445     with open(fullname, 'rb') as fh:
  446         assert fh.read() == b'\0foocom\n'
  447         
  448 def tst_open_unlink(mnt_dir):
  449     name = pjoin(mnt_dir, name_generator())
  450     data1 = b'foo'
  451     data2 = b'bar'
  452     fullname = pjoin(mnt_dir, name)
  453     with open(fullname, 'wb+', buffering=0) as fh:
  454         fh.write(data1)
  455         os.unlink(fullname)
  456         with pytest.raises(OSError) as exc_info:
  457             os.stat(fullname)
  458             assert exc_info.value.errno == errno.ENOENT
  459         assert name not in os.listdir(mnt_dir)
  460         fh.write(data2)
  461         fh.seek(0)
  462         assert fh.read() == data1+data2
  463 
  464 def tst_statvfs(mnt_dir):
  465     os.statvfs(mnt_dir)
  466 
  467 def tst_link(mnt_dir):
  468     name1 = pjoin(mnt_dir, name_generator())
  469     name2 = pjoin(mnt_dir, name_generator())
  470     shutil.copyfile(TEST_FILE, name1)
  471     assert filecmp.cmp(name1, TEST_FILE, False)
  472 
  473     fstat1 = os.lstat(name1)
  474     assert fstat1.st_nlink == 1
  475 
  476     os.link(name1, name2)
  477 
  478     fstat1 = os.lstat(name1)
  479     fstat2 = os.lstat(name2)
  480     assert fstat1 == fstat2
  481     assert fstat1.st_nlink == 2
  482     assert os.path.basename(name2) in os.listdir(mnt_dir)
  483     assert filecmp.cmp(name1, name2, False)
  484 
  485     # Since RELEASE requests are asynchronous, it is possible that
  486     # libfuse still considers the file to be open at this point
  487     # and (since -o hard_remove is not used) renames it instead of
  488     # deleting it. In that case, the following lstat() call will
  489     # still report an st_nlink value of 2 (cf. issue #157).
  490     os.unlink(name2)
  491 
  492     assert os.path.basename(name2) not in os.listdir(mnt_dir)
  493     with pytest.raises(FileNotFoundError):
  494         os.lstat(name2)
  495 
  496     # See above, we may have to wait until RELEASE has been
  497     # received before the st_nlink value is correct.
  498     maxwait = time.time() + 2
  499     fstat1 = os.lstat(name1)
  500     while fstat1.st_nlink == 2 and time.time() < maxwait:
  501         fstat1 = os.lstat(name1)
  502         time.sleep(0.1)
  503     assert fstat1.st_nlink == 1
  504 
  505     os.unlink(name1)
  506 
  507 def tst_readdir(src_dir, mnt_dir):
  508     newdir = name_generator()
  509 
  510     src_newdir = pjoin(src_dir, newdir)
  511     mnt_newdir = pjoin(mnt_dir, newdir)
  512     file_ = src_newdir + "/" + name_generator()
  513     subdir = src_newdir + "/" + name_generator()
  514     subfile = subdir + "/" + name_generator()
  515 
  516     os.mkdir(src_newdir)
  517     shutil.copyfile(TEST_FILE, file_)
  518     os.mkdir(subdir)
  519     shutil.copyfile(TEST_FILE, subfile)
  520 
  521     listdir_is = os.listdir(mnt_newdir)
  522     listdir_is.sort()
  523     listdir_should = [ os.path.basename(file_), os.path.basename(subdir) ]
  524     listdir_should.sort()
  525     assert listdir_is == listdir_should
  526 
  527     os.unlink(file_)
  528     os.unlink(subfile)
  529     os.rmdir(subdir)
  530     os.rmdir(src_newdir)
  531 
  532 def tst_truncate_path(mnt_dir):
  533     assert len(TEST_DATA) > 1024
  534 
  535     filename = pjoin(mnt_dir, name_generator())
  536     with open(filename, 'wb') as fh:
  537         fh.write(TEST_DATA)
  538 
  539     fstat = os.stat(filename)
  540     size = fstat.st_size
  541     assert size == len(TEST_DATA)
  542 
  543     # Add zeros at the end
  544     os.truncate(filename, size + 1024)
  545     assert os.stat(filename).st_size == size + 1024
  546     with open(filename, 'rb') as fh:
  547         assert fh.read(size) == TEST_DATA
  548         assert fh.read(1025) == b'\0' * 1024
  549 
  550     # Truncate data
  551     os.truncate(filename, size - 1024)
  552     assert os.stat(filename).st_size == size - 1024
  553     with open(filename, 'rb') as fh:
  554         assert fh.read(size) == TEST_DATA[:size-1024]
  555 
  556     os.unlink(filename)
  557 
  558 def tst_truncate_fd(mnt_dir):
  559     assert len(TEST_DATA) > 1024
  560     with NamedTemporaryFile('w+b', 0, dir=mnt_dir) as fh:
  561         fd = fh.fileno()
  562         fh.write(TEST_DATA)
  563         fstat = os.fstat(fd)
  564         size = fstat.st_size
  565         assert size == len(TEST_DATA)
  566 
  567         # Add zeros at the end
  568         os.ftruncate(fd, size + 1024)
  569         assert os.fstat(fd).st_size == size + 1024
  570         fh.seek(0)
  571         assert fh.read(size) == TEST_DATA
  572         assert fh.read(1025) == b'\0' * 1024
  573 
  574         # Truncate data
  575         os.ftruncate(fd, size - 1024)
  576         assert os.fstat(fd).st_size == size - 1024
  577         fh.seek(0)
  578         assert fh.read(size) == TEST_DATA[:size-1024]
  579 
  580 def tst_utimens(mnt_dir, ns_tol=0):
  581     filename = pjoin(mnt_dir, name_generator())
  582     os.mkdir(filename)
  583     fstat = os.lstat(filename)
  584 
  585     atime = fstat.st_atime + 42.28
  586     mtime = fstat.st_mtime - 42.23
  587     if sys.version_info < (3,3):
  588         os.utime(filename, (atime, mtime))
  589     else:
  590         atime_ns = fstat.st_atime_ns + int(42.28*1e9)
  591         mtime_ns = fstat.st_mtime_ns - int(42.23*1e9)
  592         os.utime(filename, None, ns=(atime_ns, mtime_ns))
  593 
  594     fstat = os.lstat(filename)
  595 
  596     assert abs(fstat.st_atime - atime) < 1e-3
  597     assert abs(fstat.st_mtime - mtime) < 1e-3
  598     if sys.version_info >= (3,3):
  599         assert abs(fstat.st_atime_ns - atime_ns) <= ns_tol
  600         assert abs(fstat.st_mtime_ns - mtime_ns) <= ns_tol
  601 
  602 def tst_passthrough(src_dir, mnt_dir):
  603     name = name_generator()
  604     src_name = pjoin(src_dir, name)
  605     mnt_name = pjoin(src_dir, name)
  606     assert name not in os.listdir(src_dir)
  607     assert name not in os.listdir(mnt_dir)
  608     with open(src_name, 'w') as fh:
  609         fh.write('Hello, world')
  610     assert name in os.listdir(src_dir)
  611     assert name in os.listdir(mnt_dir)
  612     assert os.stat(src_name) == os.stat(mnt_name)
  613 
  614     name = name_generator()
  615     src_name = pjoin(src_dir, name)
  616     mnt_name = pjoin(src_dir, name)
  617     assert name not in os.listdir(src_dir)
  618     assert name not in os.listdir(mnt_dir)
  619     with open(mnt_name, 'w') as fh:
  620         fh.write('Hello, world')
  621     assert name in os.listdir(src_dir)
  622     assert name in os.listdir(mnt_dir)
  623     assert os.stat(src_name) == os.stat(mnt_name)
  624 
  625 # avoid warning about unused import
  626 test_printcap
  627 
  628