From fc4f19091aa346b694370503d05c0e247fba055b Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Jan 15 2018 16:30:04 +0000 Subject: Introduce tests.util module All the helper functions and constants moved to new util module. This module provides: - util.start_daemon() - starts a sanlock daemon process. - util.wait_for_daemon() - wait until sanlock daemon is ready. - util.sanlock() - run sanlock and return the output. Useful for running "sanlock client ..." or "sanlock direct ..." commands. - util.wait_for_termination - wait until a process terminates within a timeout. Signed-off-by: Nir Soffer --- diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/__init__.py diff --git a/tests/daemon_test.py b/tests/daemon_test.py index af1e890..d84b838 100644 --- a/tests/daemon_test.py +++ b/tests/daemon_test.py @@ -2,83 +2,24 @@ Test sanlock client operations. """ -import errno import io -import os import signal -import socket import struct import subprocess -import time import pytest -tests_dir = os.path.dirname(__file__) -SANLOCK = os.path.join(tests_dir, os.pardir, "src", "sanlock") - -ENV = dict(os.environ) -ENV["SANLOCK_RUN_DIR"] = "/tmp/sanlock" -ENV["SANLOCK_PRIVILEGED"] = "0" - - -class TimeoutExpired(Exception): - """ Raised when timeout expired """ - - -def start_sanlock_daemon(): - cmd = [SANLOCK, "daemon", - # no fork and print all logging to stderr - "-D", - # don't use watchdog through wdmd - "-w", "0", - # don't use mlockall - "-l", "0", - # don't use high priority (RR) scheduling - "-h", "0", - # run as current user instead of "sanlock" - "-U", ENV["USER"], - "-G", ENV["USER"]] - return subprocess.Popen(cmd, env=ENV) - - -def wait_for_socket(timeout): - """ Wait until deamon is accepting connections """ - deadline = time.time() + timeout - path = os.path.join(ENV["SANLOCK_RUN_DIR"], "sanlock.sock") - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - try: - while True: - try: - s.connect(path) - return - except socket.error as e: - if e[0] not in (errno.ECONNREFUSED, errno.ENOENT): - raise # Unexpected error - if time.time() > deadline: - raise TimeoutExpired - time.sleep(0.05) - finally: - s.close() - - -def wait_for_termination(p, timeout): - """ - Wait until process terminates, or timeout expires. - """ - deadline = time.time() + timeout - while True: - if p.poll() is not None: - return - if time.time() > deadline: - raise TimeoutExpired - time.sleep(0.05) +from . import util @pytest.fixture def sanlock_daemon(): - p = start_sanlock_daemon() + """ + Run sanlock daemon during a test. + """ + p = util.start_daemon() try: - wait_for_socket(0.5) + util.wait_for_daemon(0.5) yield finally: p.terminate() @@ -87,10 +28,10 @@ def sanlock_daemon(): def test_single_instance(sanlock_daemon): # Starting another instance while the daemon must fail. - p = start_sanlock_daemon() + p = util.start_daemon() try: - wait_for_termination(p, 1.0) - except TimeoutExpired: + util.wait_for_termination(p, 1.0) + except util.TimeoutExpired: p.kill() p.wait() assert p.returncode == 1 @@ -99,9 +40,9 @@ def test_single_instance(sanlock_daemon): def test_start_after_kill(): # After killing the daemon, next instance should be able to start. for i in range(5): - p = start_sanlock_daemon() + p = util.start_daemon() try: - wait_for_socket(0.5) + util.wait_for_daemon(0.5) finally: p.kill() p.wait() @@ -111,7 +52,7 @@ def test_start_after_kill(): def test_client_failure(): # No daemon is running, client must fail with pytest.raises(subprocess.CalledProcessError) as e: - run(SANLOCK, "client", "status") + util.sanlock("client", "status") assert e.value.returncode == 1 @@ -122,7 +63,7 @@ def test_init_lockspace(tmpdir, sanlock_daemon): f.write(b"x" * 1024**2 + b"X" * 512) lockspace = "name:1:%s:0" % path - run(SANLOCK, "client", "init", "-s", lockspace) + util.sanlock("client", "init", "-s", lockspace) with io.open(str(path), "rb") as f: magic, = struct.unpack("< I", f.read(4)) @@ -133,7 +74,3 @@ def test_init_lockspace(tmpdir, sanlock_daemon): # Do not modify data after the lockspace area. f.seek(1024**2, io.SEEK_SET) assert f.read(512) == b"X" * 512 - - -def run(*args): - return subprocess.check_output(args, env=ENV) diff --git a/tests/direct_test.py b/tests/direct_test.py index 3536f5e..fa7c572 100644 --- a/tests/direct_test.py +++ b/tests/direct_test.py @@ -3,12 +3,9 @@ Test sanlock direct options. """ import io -import os import struct -import subprocess -tests_dir = os.path.dirname(__file__) -SANLOCK = os.path.join(tests_dir, os.pardir, "src", "sanlock") +from . import util def test_init_lockspace(tmpdir): @@ -18,7 +15,7 @@ def test_init_lockspace(tmpdir): f.write(b"x" * 1024**2 + b"X" * 512) lockspace = "name:1:%s:0" % path - run(SANLOCK, "direct", "init", "-s", lockspace) + util.sanlock("direct", "init", "-s", lockspace) with io.open(str(path), "rb") as f: magic, = struct.unpack("< I", f.read(4)) @@ -29,7 +26,3 @@ def test_init_lockspace(tmpdir): # Do not modify data after the lockspace area. f.seek(1024**2, io.SEEK_SET) assert f.read(512) == b"X" * 512 - - -def run(*args): - return subprocess.check_output(args) diff --git a/tests/util.py b/tests/util.py new file mode 100644 index 0000000..002425b --- /dev/null +++ b/tests/util.py @@ -0,0 +1,81 @@ +""" +Testing utilities +""" + +import errno +import os +import socket +import subprocess +import time + +TESTDIR = os.path.dirname(__file__) +SANLOCK = os.path.join(TESTDIR, os.pardir, "src", "sanlock") + +ENV = dict(os.environ) +ENV["SANLOCK_RUN_DIR"] = "/tmp/sanlock" +ENV["SANLOCK_PRIVILEGED"] = "0" + + +class TimeoutExpired(Exception): + """ Raised when timeout expired """ + + +def start_daemon(): + cmd = [SANLOCK, "daemon", + # no fork and print all logging to stderr + "-D", + # don't use watchdog through wdmd + "-w", "0", + # don't use mlockall + "-l", "0", + # don't use high priority (RR) scheduling + "-h", "0", + # run as current user instead of "sanlock" + "-U", ENV["USER"], + "-G", ENV["USER"]] + return subprocess.Popen(cmd, env=ENV) + + +def wait_for_daemon(timeout): + """ + Wait until deamon is accepting connections + """ + deadline = time.time() + timeout + path = os.path.join(ENV["SANLOCK_RUN_DIR"], "sanlock.sock") + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + while True: + try: + s.connect(path) + return + except socket.error as e: + if e[0] not in (errno.ECONNREFUSED, errno.ENOENT): + raise # Unexpected error + if time.time() > deadline: + raise TimeoutExpired + time.sleep(0.05) + finally: + s.close() + + +def sanlock(*args): + """ + Run sanlock returning the process stdout, or raising + subprocess.CalledProcessError on failures. + """ + cmd = [SANLOCK] + cmd.extend(args) + return subprocess.check_output(cmd, env=ENV) + + +def wait_for_termination(p, timeout): + """ + Wait until process terminates, or timeout expires. + """ + deadline = time.time() + timeout + while True: + if p.poll() is not None: + return + if time.time() > deadline: + raise TimeoutExpired + time.sleep(0.05)