Makefile.in | 9 ++++ tests/conftest.py | 114 +++++++++++++++++++++++++++++++++++++++++++++ tests/httpd.conf | 19 ++++++++ tests/run_tests.py | 78 +++++++++++++++++++++++++++++++ tests/test_env_daemon.py | 34 ++++++++++++++ tests/test_env_embedded.py | 36 ++++++++++++++ 6 files changed, 290 insertions(+) diff --git a/Makefile.in b/Makefile.in index 8ea2be2..0ca14f9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -58,3 +58,12 @@ distclean : clean realclean : distclean -rm -f configure + +DSOFILE = mod_wsgi.so +ifeq ($(PYTHON), python3) + DSOFILE = mod_wsgi-py3.so +endif + +check : + PYTHON=$(PYTHON) $(PYTHON) ./tests/run_tests.py --sodir \ + $(DESTDIR)$(LIBEXECDIR) --dsofile=$(DSOFILE) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100755 index 0000000..5214f1e --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,114 @@ +""" +Configuration for pytest +""" + +import time +import os +import shutil +import subprocess +import signal +import pytest + + +def pytest_addoption(parser): + """ + Add supported options + """ + parser.addoption("--sodir", + default='{}/src/server/.libs'.format(os.getcwd()), + help='dir path to mod_wsgi so file') + parser.addoption("--dsofile", default='mod_wsgi.so', + help='mod_wsgi so file name') + + +def setup_http(testdir, so_dir, so_filename): + """ + Setup httpd server's config + """ + httpdir = os.path.join(testdir, 'httpd') + logsubdir = 'logs' + if os.path.exists(httpdir): + shutil.rmtree(httpdir) + os.makedirs(httpdir) + os.mkdir(os.path.join(httpdir, 'html')) + os.mkdir(os.path.join(httpdir, logsubdir)) + + apache_mod_dir = subprocess.check_output( + ["apxs2", "-q", "libexecdir"]).strip() + + if not os.path.exists(apache_mod_dir): + raise ValueError("Could not find Apache module directory!") + os.symlink(apache_mod_dir, os.path.join(httpdir, 'modules')) + + shutil.copy('{}/{}'.format(so_dir, so_filename), httpdir) + + with open('tests/httpd.conf') as hfile: + text = hfile.read().format(HTTPROOT=httpdir, + HTTPNAME=os.environ['NSS_WRAPPER_HOSTNAME'], + HTTPADDR=os.environ['WRAP_IPADDR'], + HTTPPORT=os.environ['WRAP_PORT'], + LOGDIR=logsubdir, + ERROR_LOG='error_log', + DSOFILE=so_filename) + config = os.path.join(httpdir, 'httpd.conf') + with open(config, 'w+') as cfile: + cfile.write(text) + + test_subdirs = ['environ-daemon', 'environ-embedded'] + for subdir in test_subdirs: + bandir = os.path.join(testdir, 'httpd', 'html', subdir) + os.mkdir(bandir) + shutil.copy('tests/environ.wsgi', bandir) + + +def run_http(testdir): + """ + Run httpd server + """ + httpdir = os.path.join(testdir, 'httpd') + config = os.path.join(httpdir, 'httpd.conf') + httpd = "httpd2" + httpproc = subprocess.Popen([httpd, '-DFOREGROUND', '-f', config], + preexec_fn=os.setsid) + if httpproc.poll() is None: + # check whether httpd server ready for processing or not + retry_number = 0 + max_retries = 20 + httpd_logfile = os.path.join(httpdir, 'logs', 'error_log') + for i in range(max_retries): + try: + with open(httpd_logfile, 'r') as logfile: + lines = [line for line in logfile + if 'Command line' in line] + if not lines: + time.sleep(0.5) + retry_number = i + 1 + else: + break + except IOError: + time.sleep(0.5) + retry_number = i + 1 + if retry_number >= max_retries: + raise AssertionError( + "Perphaps httpd server was not started correctly") + return httpproc + + +@pytest.fixture(scope='module') +def prepare_tests(request): + """ + Preparation for module tests + """ + testdir = '{}/testsdir'.format(os.getcwd()) + so_dir = request.config.getoption('--sodir') + so_filename = request.config.getoption('--dsofile') + + setup_http(testdir, so_dir, so_filename) + httpproc = run_http(testdir) + + def resource_teardown(): + """ + Clean up module tests + """ + os.killpg(httpproc.pid, signal.SIGTERM) + request.addfinalizer(resource_teardown) diff --git a/tests/httpd.conf b/tests/httpd.conf new file mode 100644 index 0000000..748de40 --- /dev/null +++ b/tests/httpd.conf @@ -0,0 +1,19 @@ +PidFile "{HTTPROOT}/{LOGDIR}/httpd.pid" + +ServerRoot "{HTTPROOT}" +ServerName "{HTTPNAME}" +Listen {HTTPADDR}:{HTTPPORT} +DocumentRoot "{HTTPROOT}/html" +ErrorLog "{LOGDIR}/{ERROR_LOG}" + +LoadModule wsgi_module "{DSOFILE}" +LoadModule authz_core_module modules/mod_authz_core.so + +WSGIScriptAlias /daemon-env/ {HTTPROOT}/html/environ-daemon/environ.wsgi +WSGIDaemonProcess wsgi-test processes=2 threads=15 +WSGISocketPrefix {HTTPROOT} + + WSGIProcessGroup wsgi-test + + +WSGIScriptAlias /embedded-env/ {HTTPROOT}/html/environ-embedded/environ.wsgi diff --git a/tests/run_tests.py b/tests/run_tests.py new file mode 100755 index 0000000..1cf7cdb --- /dev/null +++ b/tests/run_tests.py @@ -0,0 +1,78 @@ +""" +Wrapper script for pytest runner +""" + +import argparse +import os +import shutil +import subprocess + +WRAP_HOSTNAME = "wsgi.dev.test" +WRAP_IPADDR = '127.0.0.10' +WRAP_PORT = '80' + + +def parse_args(): + """ + Script's arguments parser + """ + parser = argparse.ArgumentParser( + description='Mod WSGI Tests Environment') + parser.add_argument('--sodir', + default='{}/src/server/.libs'.format(os.getcwd()), + help="mod_wsgi object dirpath") + parser.add_argument('--dsofile', default='mod_wsgi.so', + help="mod_wsgi object filename") + return vars(parser.parse_args()) + + +def setup_wrappers(basedir): + """ + Setup nss and socket wrappers + """ + wrapdir = os.path.join(basedir, 'wrapdir') + if not os.path.exists(wrapdir): + os.makedirs(wrapdir) + + hosts_file = os.path.join(basedir, 'hosts') + with open(hosts_file, 'w+') as hfile: + hfile.write('{} {}\n'.format(WRAP_IPADDR, WRAP_HOSTNAME)) + + wenv = {'LD_PRELOAD': 'libsocket_wrapper.so libnss_wrapper.so', + 'SOCKET_WRAPPER_DIR': wrapdir, + 'SOCKET_WRAPPER_DEFAULT_IFACE': '10', + 'WRAP_IPADDR': WRAP_IPADDR, + 'WRAP_PORT': WRAP_PORT, + 'NSS_WRAPPER_HOSTNAME': WRAP_HOSTNAME, + 'NSS_WRAPPER_HOSTS': hosts_file} + return wenv + + +def main(): + """ + Wrapper script for pytest runner + """ + args = parse_args() + + testdir = '{}/testsdir'.format(os.getcwd()) + so_dir = args['sodir'] + so_filename = args['dsofile'] + if os.path.exists(testdir): + shutil.rmtree(testdir) + os.makedirs(testdir) + + wrapenv = setup_wrappers(testdir) + + testenv = {'PATH': '/sbin:/bin:/usr/sbin:/usr/bin', } + testenv.update(wrapenv) + + python = os.environ['PYTHON'] + testproc = subprocess.Popen([python, "-m", "pytest", "-v", "tests", + "--sodir", so_dir, "--dsofile", so_filename], + env=testenv, preexec_fn=os.setsid) + testproc.wait() + exit(testproc.returncode) + + +if __name__ == '__main__': + main() diff --git a/tests/test_env_daemon.py b/tests/test_env_daemon.py new file mode 100755 index 0000000..9f46e98 --- /dev/null +++ b/tests/test_env_daemon.py @@ -0,0 +1,34 @@ +""" +Test daemon mode environ +""" + +import os +import requests +import pytest + + +def find_result(result, key): + """ + Helper for find key in result + """ + result_text = result.splitlines() + result_list = [x.split(': ') for x in filter(None, result_text)] + result_dict = {x[0]: x[1] for x in [val for val in result_list] + if len(x) == 2} + try: + return result_dict[key] + except KeyError: + return '' + + +@pytest.mark.usefixtures("prepare_tests") +def test_daemon_env(): + """ + Test daemon mode environ + """ + url = 'http://{hostname}/daemon-env/'.format( + hostname=os.environ['NSS_WRAPPER_HOSTNAME']) + result = requests.get(url) + assert result.status_code == 200 + assert find_result(result.text, "mod_wsgi.script_name") == "'/daemon-env/'" + assert find_result(result.text, "mod_wsgi.process_group") == "'wsgi-test'" diff --git a/tests/test_env_embedded.py b/tests/test_env_embedded.py new file mode 100755 index 0000000..fc63f82 --- /dev/null +++ b/tests/test_env_embedded.py @@ -0,0 +1,36 @@ +""" +Test embedded mode environ +""" + +import os +import requests +import pytest + + +def find_result(result, key): + """ + Helper for find key in result + """ + result_text = result.splitlines() + result_list = [x.split(': ') for x in filter(None, result_text)] + result_dict = {x[0]: x[1] for x in [val for val in result_list] + if len(x) == 2} + try: + return result_dict[key] + except KeyError: + return '' + + +@pytest.mark.usefixtures("prepare_tests") +def test_embedded_env(): + """ + Test daemon mode environ + """ + url = 'http://{hostname}/embedded-env/'.format( + hostname=os.environ['NSS_WRAPPER_HOSTNAME']) + result = requests.get(url) + assert result.status_code == 200 + assert find_result( + result.text, "mod_wsgi.script_name" + ) == "'/embedded-env/'" + assert find_result(result.text, "mod_wsgi.process_group") == "''"