Skip to content

Commit

Permalink
Added unit tests and travis integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikratio committed Jun 22, 2017
1 parent b66ecb9 commit 5f4619b
Show file tree
Hide file tree
Showing 15 changed files with 698 additions and 6 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Expand Up @@ -6,8 +6,6 @@
# NOTE! Please use 'git ls-files -i --exclude-standard'
# command after changing this file, to see if there are
# any tracked files which get ignored after the change.
.*
!.gitignore
*.o
*.lo
*.la
Expand Down Expand Up @@ -35,3 +33,4 @@ sshfs.1
/.pc
/patches
/m4
.deps/
20 changes: 20 additions & 0 deletions .travis.yml
@@ -0,0 +1,20 @@
sudo: required
dist: trusty

language:
- c
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- valgrind
- clang
- gcc
- gcc-6
- fuse
- libfuse2
- libfuse-dev
install: test/travis-install.sh
script: test/travis-build.sh

1 change: 1 addition & 0 deletions ChangeLog.rst
@@ -1,6 +1,7 @@
Unreleased Changes
------------------

* Added unit tests
* Documented limited hardlink support.
* Added support for building with Meson.
* Added support for more SSH options.
Expand Down
13 changes: 9 additions & 4 deletions README.rst
Expand Up @@ -48,26 +48,31 @@ be available from your operating system's package manager).

To build and install, we recommend to use Meson_ (version 0.38 or
newer) and Ninja_. After extracting the sshfs tarball, create a
(temporary) build directory and run Meson and Ninja::
(temporary) build directory and run Meson::

$ md build; cd build
$ meson ..
$ ninja
$ sudo ninja install

Normally, the default build options will work fine. If you
nevertheless want to adjust them, you can do so with the *mesonconf*
command::

$ mesonconf # list options
$ mesonconf -D strip=true # set an option
$ ninja # rebuild

To build, test and install SSHFS, you then use Ninja (running the
tests requires the `py.test`_ Python module)::

$ ninja
$ python3 -m pytest test/ # optional, but recommended
$ sudo ninja install

.. _libfuse: http://github.com/libfuse/libfuse
.. _OSXFUSE: https://osxfuse.github.io/
.. _Glib: https://developer.gnome.org/glib/stable/
.. _Meson: http://mesonbuild.com/
.. _Ninja: https://ninja-build.org/
.. _`py.test`: http://www.pytest.org/

Alternate Installation
----------------------
Expand Down
3 changes: 3 additions & 0 deletions meson.build
Expand Up @@ -59,3 +59,6 @@ executable('sshfs', sshfs_sources,
# This is a little ugly. Is there a better way to tell Meson that the
# manpage is in the build directory?
install_man(join_paths(meson.current_build_dir(), 'sshfs.1'))

subdir('test')

1 change: 1 addition & 0 deletions test/.gitignore
@@ -0,0 +1 @@
__pycache__/
89 changes: 89 additions & 0 deletions test/conftest.py
@@ -0,0 +1,89 @@
import sys
import pytest
import time
import re

# If a test fails, wait a moment before retrieving the captured
# stdout/stderr. When using a server process, this makes sure that we capture
# any potential output of the server that comes *after* a test has failed. For
# example, if a request handler raises an exception, the server first signals an
# error to FUSE (causing the test to fail), and then logs the exception. Without
# the extra delay, the exception will go into nowhere.
@pytest.mark.hookwrapper
def pytest_pyfunc_call(pyfuncitem):
outcome = yield
failed = outcome.excinfo is not None
if failed:
time.sleep(1)

@pytest.fixture()
def pass_capfd(request, capfd):
'''Provide capfd object to UnitTest instances'''
request.instance.capfd = capfd

def check_test_output(capfd):
(stdout, stderr) = capfd.readouterr()

# Write back what we've read (so that it will still be printed.
sys.stdout.write(stdout)
sys.stderr.write(stderr)

# Strip out false positives
for (pattern, flags, count) in capfd.false_positives:
cp = re.compile(pattern, flags)
(stdout, cnt) = cp.subn('', stdout, count=count)
if count == 0 or count - cnt > 0:
stderr = cp.sub('', stderr, count=count - cnt)

patterns = [ r'\b{}\b'.format(x) for x in
('exception', 'error', 'warning', 'fatal', 'traceback',
'fault', 'crash(?:ed)?', 'abort(?:ed)',
'uninitiali[zs]ed') ]
patterns += ['^==[0-9]+== ']
for pattern in patterns:
cp = re.compile(pattern, re.IGNORECASE | re.MULTILINE)
hit = cp.search(stderr)
if hit:
raise AssertionError('Suspicious output to stderr (matched "%s")' % hit.group(0))
hit = cp.search(stdout)
if hit:
raise AssertionError('Suspicious output to stdout (matched "%s")' % hit.group(0))

def register_output(self, pattern, count=1, flags=re.MULTILINE):
'''Register *pattern* as false positive for output checking
This prevents the test from failing because the output otherwise
appears suspicious.
'''

self.false_positives.append((pattern, flags, count))

# This is a terrible hack that allows us to access the fixtures from the
# pytest_runtest_call hook. Among a lot of other hidden assumptions, it probably
# relies on tests running sequential (i.e., don't dare to use e.g. the xdist
# plugin)
current_capfd = None
@pytest.yield_fixture(autouse=True)
def save_cap_fixtures(request, capfd):
global current_capfd
capfd.false_positives = []

# Monkeypatch in a function to register false positives
type(capfd).register_output = register_output

if request.config.getoption('capture') == 'no':
capfd = None
current_capfd = capfd
bak = current_capfd
yield

# Try to catch problems with this hack (e.g. when running tests
# simultaneously)
assert bak is current_capfd
current_capfd = None

@pytest.hookimpl(trylast=True)
def pytest_runtest_call(item):
capfd = current_capfd
if capfd is not None:
check_test_output(capfd)
11 changes: 11 additions & 0 deletions test/lsan_suppress.txt
@@ -0,0 +1,11 @@
# Suppression file for address sanitizer.

# There are some leaks in command line option parsing. They should be
# fixed at some point, but are harmless since the consume just a small,
# constant amount of memory and do not grow.
leak:fuse_opt_parse


# Leaks in fusermount3 are harmless as well (it's a short-lived
# process) - but patches are welcome!
leak:fusermount.c
11 changes: 11 additions & 0 deletions test/meson.build
@@ -0,0 +1,11 @@
test_scripts = [ 'conftest.py', 'pytest.ini', 'test_sshfs.py',
'util.py' ]
custom_target('test_scripts', input: test_scripts,
output: test_scripts, build_by_default: true,
command: ['cp', '-fPu', '--preserve=mode',
'@INPUT@', meson.current_build_dir() ])

# Provide something helpful when running 'ninja test'
wrong_cmd = executable('wrong_command', 'wrong_command.c',
install: false)
test('wrong_cmd', wrong_cmd)
2 changes: 2 additions & 0 deletions test/pytest.ini
@@ -0,0 +1,2 @@
[pytest]
addopts = --verbose --assert=rewrite --tb=native -x -r a

0 comments on commit 5f4619b

Please sign in to comment.