Change test executables into modules
authorSimo Sorce <simo@redhat.com>
Mon, 16 Jun 2014 20:26:31 +0000 (16:26 -0400)
committerSimo Sorce <simo@redhat.com>
Tue, 17 Jun 2014 18:51:44 +0000 (14:51 -0400)
Create a common tests framework and convert tests into modules loaded
at runtime using the ipsilon plugin framework.

Signed-off-by: Simo Sorce <simo@redhat.com>
Makefile
tests/helpers/common.py [new file with mode: 0755]
tests/test1.cfg [deleted file]
tests/test1.py
tests/tests.py

index 0d7e01a..fc8795a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -39,9 +39,18 @@ lp-test:
                   ./tests
        pep8 tests
 
-test: lp-test
+wrappers:
+       #rm -fr wrapdir
+       #mkdir wrapdir
+       #LD_PRELOAD=libsocket_wrapper.so
+       #SOCKET_WRAPPER_DIR=wrapdir
+       #SOCKET_WRAPPER_DEFAULT_IFACE=9
+
+tests: wrappers
+       PYTHONPATH=./ ./tests/tests.py --test=test1
+
+test: lp-test tests
        PYTHONPATH=./ ./ipsilon/tools/saml2metadata.py
-       ./tests/tests.py --test=test1
 
 sdist:
        python setup.py sdist
diff --git a/tests/helpers/common.py b/tests/helpers/common.py
new file mode 100755 (executable)
index 0000000..d020a3c
--- /dev/null
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014  Simo Sorce <simo@redhat.com>
+#
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import ConfigParser
+import io
+from ipsilon.util.plugin import PluginObject
+import os
+import pwd
+import shutil
+import signal
+from string import Template
+import subprocess
+
+
+class IpsilonTestBase(PluginObject):
+
+    def __init__(self, name, execname):
+        super(IpsilonTestBase, self).__init__()
+        self.name = name
+        self.execname = execname
+        self.rootdir = os.getcwd()
+        self.testdir = None
+        self.testuser = pwd.getpwuid(os.getuid())[0]
+        self.processes = []
+
+    def force_remove(self, op, name, info):
+        os.chmod(name, 0700)
+        os.remove(name)
+
+    def setup_base(self, path, test):
+        self.testdir = os.path.join(path, test.name)
+        if os.path.exists(self.testdir):
+            shutil.rmtree(self.testdir, onerror=self.force_remove)
+        os.makedirs(self.testdir)
+        shutil.copytree(os.path.join(self.rootdir, 'templates'),
+                        os.path.join(self.testdir, 'templates'))
+        os.mkdir(os.path.join(self.testdir, 'etc'))
+        os.mkdir(os.path.join(self.testdir, 'lib'))
+        os.mkdir(os.path.join(self.testdir, 'lib', test.name))
+        os.mkdir(os.path.join(self.testdir, 'log'))
+
+    def generate_profile(self, global_opts, args_opts, name, addr, port):
+        newconf = ConfigParser.ConfigParser()
+        newconf.add_section('globals')
+        for k in global_opts.keys():
+            newconf.set('globals', k, global_opts[k])
+        newconf.add_section('arguments')
+        for k in args_opts.keys():
+            newconf.set('arguments', k, args_opts[k])
+
+        profile = io.BytesIO()
+        newconf.write(profile)
+
+        t = Template(profile.getvalue())
+        text = t.substitute({'NAME': name, 'ADDRESS': addr, 'PORT': port,
+                             'TESTDIR': self.testdir,
+                             'ROOTDIR': self.rootdir,
+                             'TEST_USER': self.testuser})
+
+        filename = os.path.join(self.testdir, '%s_profile.cfg' % name)
+        with open(filename, 'wb') as f:
+            f.write(text)
+
+        return filename
+
+    def setup_http(self, name, addr, port):
+        httpdir = os.path.join(self.testdir, name)
+        os.mkdir(httpdir)
+        os.mkdir(os.path.join(httpdir, 'conf.d'))
+        os.mkdir(os.path.join(httpdir, 'html'))
+        os.mkdir(os.path.join(httpdir, 'logs'))
+        os.symlink('/etc/httpd/modules', os.path.join(httpdir, 'modules'))
+
+        with open(os.path.join(self.rootdir, 'tests/httpd.conf')) as f:
+            t = Template(f.read())
+            text = t.substitute({'HTTPROOT': httpdir,
+                                 'HTTPADDR': addr,
+                                 'HTTPPORT': port})
+        filename = os.path.join(httpdir, 'httpd.conf')
+        with open(filename, 'w+') as f:
+            f.write(text)
+
+        return filename
+
+    def setup_idp_server(self, profile, name, addr, port, env):
+        http_conf_file = self.setup_http(name, addr, port)
+        cmd = [os.path.join(self.rootdir,
+                            'ipsilon/install/ipsilon-server-install'),
+               '--config-profile=%s' % profile]
+        subprocess.check_call(cmd, env=env)
+        os.symlink(os.path.join(self.rootdir, 'ipsilon'),
+                   os.path.join(self.testdir, 'lib', name, 'ipsilon'))
+
+        return http_conf_file
+
+    def setup_sp_server(self, profile, name, addr, port, env):
+        http_conf_file = self.setup_http(name, addr, port)
+        cmd = [os.path.join(self.rootdir,
+                            'ipsilon/install/ipsilon-client-install'),
+               '--config-profile=%s' % profile]
+        subprocess.check_call(cmd, env=env)
+
+        return http_conf_file
+
+    def start_http_server(self, conf, env):
+        p = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND', '-f', conf],
+                             env=env, preexec_fn=os.setsid)
+        self.processes.append(p)
+
+    def wait(self):
+        for p in self.processes:
+            os.killpg(p.pid, signal.SIGTERM)
+
+    def setup_servers(self, env=None):
+        raise NotImplementedError()
+
+    def run(self, env):
+        exe = self.execname
+        if exe.endswith('c'):
+            exe = exe[:-1]
+        return subprocess.call([exe], env=env)
diff --git a/tests/test1.cfg b/tests/test1.cfg
deleted file mode 100644 (file)
index 01402f5..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-[tests]
-servers=idp1:127.0.0.10:45080
-clients=sp1:127.0.0.11:45081
-
-[idp1_globals]
-TEMPLATES=${TESTDIR}/templates/install
-CONFDIR=${TESTDIR}/etc
-DATADIR=${TESTDIR}/lib
-HTTPDCONFD=${TESTDIR}/idp1/conf.d
-STATICDIR=${ROOTDIR}
-BINDIR=${ROOTDIR}/ipsilon
-WSGI_SOCKET_PREFIX=${TESTDIR}/idp1/logs/wsgi
-
-[idp1_arguments]
-hostname=127.0.0.10:45080
-admin_user=${TEST_USER}
-system_user=${TEST_USER}
-instance=idp1
-secure=no
-testauth=yes
-pam=no
-krb=no
-ipa=no
-server_debugging=True
-
-[sp1_globals]
-HTTPDCONFD=${TESTDIR}/sp1/conf.d
-SAML2_TEMPLATE=${TESTDIR}/templates/install/saml2/sp.conf
-SAML2_CONFFILE=${TESTDIR}/sp1/conf.d/ipsilon-saml.conf
-SAML2_HTTPDIR=${TESTDIR}/sp1/saml2
-
-[sp1_arguments]
-hostname=127.0.0.11:45081
-saml_idp_metadata=http://127.0.0.10:45080/idp1/saml2/metadata
-saml_secure_setup=False
-saml_auth=/sp
-httpd_user=${TEST_USER}
index 411ac6e..34b9c88 100755 (executable)
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
-from helpers import http  # pylint: disable=relative-import
+from helpers.common import IpsilonTestBase  # pylint: disable=relative-import
+from helpers.http import HttpSessions  # pylint: disable=relative-import
 import os
 import pwd
 import sys
+from string import Template
+
+
+idp_g = {'TEMPLATES': '${TESTDIR}/templates/install',
+         'CONFDIR': '${TESTDIR}/etc',
+         'DATADIR': '${TESTDIR}/lib',
+         'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d',
+         'STATICDIR': '${ROOTDIR}',
+         'BINDIR': '${ROOTDIR}/ipsilon',
+         'WSGI_SOCKET_PREFIX': '${TESTDIR}/${NAME}/logs/wsgi'}
+
+
+idp_a = {'hostname': '${ADDRESS}:${PORT}',
+         'admin_user': '${TEST_USER}',
+         'system_user': '${TEST_USER}',
+         'instance': '${NAME}',
+         'secure': 'no',
+         'testauth': 'yes',
+         'pam': 'no',
+         'krb': 'no',
+         'ipa': 'no',
+         'server_debugging': 'True'}
+
+
+sp_g = {'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d',
+        'SAML2_TEMPLATE': '${TESTDIR}/templates/install/saml2/sp.conf',
+        'SAML2_CONFFILE': '${TESTDIR}/${NAME}/conf.d/ipsilon-saml.conf',
+        'SAML2_HTTPDIR': '${TESTDIR}/${NAME}/saml2'}
+
+
+sp_a = {'hostname': '${ADDRESS}:${PORT}',
+        'saml_idp_metadata': 'http://127.0.0.10:45080/idp1/saml2/metadata',
+        'saml_secure_setup': 'False',
+        'saml_auth': '/sp',
+        'httpd_user': '${TEST_USER}'}
+
+
+def fixup_sp_httpd(httpdir):
+    location = """
+
+Alias /sp ${HTTPDIR}/sp
+
+<Directory ${HTTPDIR}/sp>
+    Require all granted
+</Directory>
+"""
+    index = """WORKS!"""
+
+    t = Template(location)
+    text = t.substitute({'HTTPDIR': httpdir})
+    with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f:
+        f.write(text)
+
+    os.mkdir(httpdir + '/sp')
+    with open(httpdir + '/sp/index.html', 'w') as f:
+        f.write(index)
+
+
+class IpsilonTest(IpsilonTestBase):
+
+    def __init__(self):
+        super(IpsilonTest, self).__init__('test1', __file__)
+
+    def setup_servers(self, env=None):
+        print "Installing IDP server"
+        name = 'idp1'
+        addr = '127.0.0.10'
+        port = '45080'
+        idp = self.generate_profile(idp_g, idp_a, name, addr, port)
+        conf = self.setup_idp_server(idp, name, addr, port, env)
+
+        print "Starting IDP's httpd server"
+        self.start_http_server(conf, env)
+
+        print "Installing SP server"
+        name = 'sp1'
+        addr = '127.0.0.11'
+        port = '45081'
+        sp = self.generate_profile(sp_g, sp_a, name, addr, port)
+        conf = self.setup_sp_server(sp, name, addr, port, env)
+        fixup_sp_httpd(os.path.dirname(conf))
+
+        print "Starting SP's httpd server"
+        self.start_http_server(conf, env)
 
 
 if __name__ == '__main__':
-    basedir = sys.argv[1]
 
     idpname = 'idp1'
     spname = 'sp1'
     user = pwd.getpwuid(os.getuid())[0]
 
-    sess = http.HttpSessions()
+    sess = HttpSessions()
     sess.add_server(idpname, 'http://127.0.0.10:45080', user, 'ipsilon')
     sess.add_server(spname, 'http://127.0.0.11:45081')
 
index 6fa880d..4690442 100755 (executable)
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import argparse
-import ConfigParser
 from datetime import datetime
+import inspect
+from ipsilon.util import plugin
 import logging
 import os
-import pwd
-import shutil
-import signal
-import subprocess
 import sys
-from string import Template
+import subprocess
+import traceback
 
 
 logger = None
 
 
+class Tests(object):
+
+    def __init__(self):
+        p = plugin.Plugins()
+        (pathname, dummy) = os.path.split(inspect.getfile(Tests))
+        self.plugins = p.get_plugins(pathname, 'IpsilonTest')
+
+
 def parse_args():
     parser = argparse.ArgumentParser(description='Ipsilon Tests Environment')
     parser.add_argument('--path', default='%s/testdir' % os.getcwd(),
@@ -46,12 +52,12 @@ def parse_args():
     return vars(parser.parse_args())
 
 
-def openlogs(path, test):
+def openlogs(path, name):
     global logger  # pylint: disable=W0603
     logger = logging.getLogger()
     try:
         datestr = datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
-        filename = '%s/test-%s-%s.log' % (path, test, datestr)
+        filename = '%s/test-%s-%s.log' % (path, name, datestr)
         lh = logging.FileHandler(filename)
     except IOError, e:
         print >> sys.stderr, 'Unable to open %s (%s)' % (filename, str(e))
@@ -62,115 +68,6 @@ def openlogs(path, test):
     logger.setLevel(logging.DEBUG)
 
 
-def force_remove(op, name, info):
-    os.chmod(name, 0700)
-    os.remove(name)
-
-
-def setup_http(httpdir, addr, port):
-    os.mkdir(httpdir)
-    os.mkdir(httpdir + '/conf.d')
-    os.mkdir(httpdir + '/html')
-    os.mkdir(httpdir + '/logs')
-    os.symlink('/etc/httpd/modules', httpdir + '/modules')
-
-    with open('tests/httpd.conf') as f:
-        t = Template(f.read())
-        text = t.substitute({'HTTPROOT': httpdir,
-                             'HTTPADDR': addr, 'HTTPPORT': port})
-    with open(httpdir + '/httpd.conf', 'w+') as f:
-        f.write(text)
-
-
-def setup_test(path, test):
-    profile = 'tests/%s.cfg' % test
-    if not os.path.exists(profile):
-        raise ValueError('Unrecognized test name [%s]' % test)
-
-    opts = {}
-    config = ConfigParser.ConfigParser()
-    config.read(profile)
-    if 'tests' not in config.sections():
-        raise ValueError('Missing [tests] in profile [%s]' % test)
-    T = config.options('tests')
-    for t in T:
-        opts[t] = config.get('tests', t)
-
-    base = '%s/%s' % (path, test)
-    if os.path.exists(base):
-        shutil.rmtree(base, onerror=force_remove)
-    os.makedirs(base)
-    shutil.copytree('templates', base + '/templates')
-    os.mkdir(base + '/etc')
-    os.mkdir(base + '/lib')
-    os.mkdir(base + '/lib/' + test)
-    os.mkdir(base + '/log')
-
-    with open(profile) as f:
-        t = Template(f.read())
-        text = t.substitute({'TESTDIR': base, 'ROOTDIR': os.getcwd(),
-                             'TEST_USER': pwd.getpwuid(os.getuid())[0]})
-    with open(base + '/profile.cfg', 'w+') as f:
-        f.write(text)
-
-    opts['basedir'] = base
-    return opts
-
-
-def generate_profile(profile, name):
-    config = ConfigParser.ConfigParser()
-    config.read(profile)
-
-    global_section = '%s_globals' % name
-    global_options = {}
-    if global_section in config.sections():
-        G = config.options(global_section)
-        for g in G:
-            global_options[g] = config.get(global_section, g)
-
-    args_section = '%s_arguments' % name
-    args_options = {}
-    if args_section in config.sections():
-        A = config.options(args_section)
-        for a in A:
-            args_options[a] = config.get(args_section, a)
-
-    newconf = ConfigParser.ConfigParser()
-    newconf.add_section('globals')
-    for k in global_options.keys():
-        newconf.set('globals', k, global_options[k])
-    newconf.add_section('arguments')
-    for k in args_options.keys():
-        newconf.set('arguments', k, args_options[k])
-
-    filename = os.path.join(os.path.dirname(profile), '%s_profile.cfg' % name)
-    with open(filename, 'wb') as f:
-        newconf.write(f)
-
-    return filename
-
-
-def fixup_sp_httpd(httpdir):
-    location = """
-
-Alias /sp ${HTTPDIR}/sp
-
-<Directory ${HTTPDIR}/sp>
-    Require all granted
-</Directory>
-"""
-    index = """WORKS!"""
-
-    t = Template(location)
-    text = t.substitute({'HTTPDIR': httpdir})
-    with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f:
-        f.write(text)
-
-    os.mkdir(httpdir + '/sp')
-    with open(httpdir + '/sp/index.html', 'w') as f:
-        f.write(index)
-
-
 def try_wrappers(base, wrappers):
     if wrappers == 'no':
         return {}
@@ -192,83 +89,38 @@ def try_wrappers(base, wrappers):
 
     return wenv
 
+
 if __name__ == '__main__':
 
     args = parse_args()
 
+    tests = Tests()
+    if args['test'] not in tests.plugins:
+        print >> sys.stderr, "Unknown test [%s]" % args['test']
+        sys.exit(1)
+    test = tests.plugins[args['test']]
+
     if not os.path.exists(args['path']):
         os.makedirs(args['path'])
+
     openlogs(args['path'], args['test'])
 
-    options = setup_test(args['path'], args['test'])
-    basedir = options['basedir']
+    test.setup_base(args['path'], test)
 
-    env = try_wrappers(basedir, args['wrappers'])
-    env['PYTHONPATH'] = './'
+    env = try_wrappers(test.testdir, args['wrappers'])
+    env['PYTHONPATH'] = test.rootdir
 
-    srvs = []
     try:
-        for h in options['servers'].split(','):
-            sname, saddr, sport = h.split(':')
-            basehttpdir = '%s/%s' % (basedir, sname)
-            setup_http(basehttpdir, saddr, sport)
-
-            print "Installing IDP server %s" % sname
-            sprofile = generate_profile('%s/profile.cfg' % basedir, sname)
-            p = subprocess.Popen(['./ipsilon/install/ipsilon-server-install',
-                                  '--config-profile=%s' % sprofile], env=env,
-                                 stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE)
-            stdout, stderr = p.communicate()
-            logger.error(stderr)
-            logger.info(stdout)
-            if p.returncode:
-                sys.exit(p.returncode)
-
-            os.symlink('%s/ipsilon' % os.getcwd(),
-                       '%s/lib/%s/ipsilon' % (basedir, sname))
-
-            print "Starting httpd server in %s" % basehttpdir
-            srv = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND',
-                                    '-f', basehttpdir + '/httpd.conf'],
-                                   env=env, preexec_fn=os.setsid)
-            srvs.append(srv)
-
-        for h in options['clients'].split(','):
-            sname, saddr, sport = h.split(':')
-            basehttpdir = '%s/%s' % (basedir, sname)
-            setup_http(basehttpdir, saddr, sport)
-
-            print "Installing SP server %s" % sname
-            sprofile = generate_profile('%s/profile.cfg' % basedir, sname)
-            p = subprocess.Popen(['./ipsilon/install/ipsilon-client-install',
-                                  '--config-profile=%s' % sprofile], env=env,
-                                 stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE)
-            stdout, stderr = p.communicate()
-            logger.error(stderr)
-            logger.info(stdout)
-            if p.returncode:
-                sys.exit(p.returncode)
-
-            fixup_sp_httpd(basehttpdir)
-
-            print "Starting httpd server in %s" % basehttpdir
-            srv = subprocess.Popen(['/usr/sbin/httpd', '-DFOREGROUND',
-                                    '-f', basehttpdir + '/httpd.conf'],
-                                   env=env, preexec_fn=os.setsid)
-            srvs.append(srv)
-
-        print "Testing installation"
-        if os.path.exists('tests/%s.py' % args['test']):
-            code = subprocess.call(['./tests/%s.py' % args['test'], basedir],
-                                   env=env)
-            if code:
-                sys.exit(code)
-    except Exception:  # pylint: disable=broad-except
+        test.setup_servers(env)
+
+        code = test.run(env)
+        if code:
+            sys.exit(code)
+    except Exception, e:  # pylint: disable=broad-except
+        print >> sys.stderr, "Error: %s" % repr(e)
+        traceback.print_exc(None, sys.stderr)
         sys.exit(1)
     finally:
-        for srv in srvs:
-            os.killpg(srv.pid, signal.SIGTERM)
+        test.wait()
 
     print "FINISHED"