diff --git a/integration/R/test_framework.py b/integration/R/test_framework.py new file mode 100644 index 0000000..c0ea6e4 --- /dev/null +++ b/integration/R/test_framework.py @@ -0,0 +1,21 @@ +import unittest +from stick import KolabPhpunitTest + +class TestFramework(KolabPhpunitTest): + """ + Run framework unit tests from upstream Roundcube + """ + def test_framework(self): + self.execute(testsuite='All Tests', message="Roundcube Framework Testsuite") + + def test_plugin_acl(self): + self.execute('plugins/acl/tests/Acl.php', message="Roundcube ACL Plugin") + + def test_plugin_managesieve(self): + self.execute('plugins/managesieve/tests/Managesieve.php', message="Roundcube Managesieve Plugin") + self.execute('plugins/managesieve/tests/Parser.php', message="Roundcube Managesieve Parser") + self.execute('plugins/managesieve/tests/Tokenizer.php', message="Roundcube Managesieve Tokenizer") + self.execute('plugins/managesieve/tests/Vacation.php', message="Roundcube Managesieve Vacation") + +if __name__ == '__main__': + unittest.main() diff --git a/integration/R/testBasics.py b/integration/R/test_selenium.py similarity index 70% rename from integration/R/testBasics.py rename to integration/R/test_selenium.py index 1066fb2..d637a22 100644 --- a/integration/R/testBasics.py +++ b/integration/R/test_selenium.py @@ -1,32 +1,32 @@ import unittest from stick import KolabPhpunitTest class TestBasics(KolabPhpunitTest): """ Run Selenium tests from upstream Roundcube """ john = None @classmethod def setUpClass(self, *args, **kw): # define user accounts required for this test self.john = self.require_user("John", "Doe") KolabPhpunitTest.setUpClass(self, *args, **kw) def setUp(self, *args, **kw): KolabPhpunitTest.setUp(self, *args, **kw) self.set_login(self.john['mail'], self.john['userpassword']) def test_addressbook(self): - self.execute(testsuite='Addressbook', message="Roundcube Addressbook Testsuite") + self.execute(testsuite='Addressbook', selenium=True, message="Roundcube Addressbook Testsuite") def test_mail(self): - self.execute(testsuite='Mail', message="Roundcube Mail Testsuite") + self.execute(testsuite='Mail', selenium=True, message="Roundcube Mail Testsuite") def test_settings(self): - self.execute(testsuite='Settings', message="Roundcube Settings Testsuite") + self.execute(testsuite='Settings', selenium=True, message="Roundcube Settings Testsuite") if __name__ == '__main__': unittest.main() diff --git a/integration/RPK/run.py b/integration/RPK/run.py new file mode 100644 index 0000000..edf4446 --- /dev/null +++ b/integration/RPK/run.py @@ -0,0 +1,15 @@ +import unittest +import stick +import sys + +from test_T153 import TestT153 + +if __name__ == '__main__': + opts = stick.conf.cli_keywords + loader = stick.KolabTestLoader(opts) + alltests = unittest.TestSuite() + + # TODO: add more tests covering the basic functionality assumed in the following use-case tests + alltests.addTests(loader.loadTestsFromTestCase(TestT153)) + + unittest.main(defaultTest='alltests', argv=sys.argv[:1]) diff --git a/integration/RPK/testT153.py b/integration/RPK/test_T153.py similarity index 100% rename from integration/RPK/testT153.py rename to integration/RPK/test_T153.py diff --git a/integration/stick/__init__.py b/integration/stick/__init__.py index 6a11c7d..736189c 100644 --- a/integration/stick/__init__.py +++ b/integration/stick/__init__.py @@ -1,36 +1,45 @@ import time import pykolab +# define additional CLI options for test runs +conf = pykolab.getConf() +conf.cli_parser.add_option('-a', '--all', dest='failfast', action='store_false', default=True) +conf.cli_parser.add_option('-t', '--todo', dest='todo', action='store_true', default=False) + +conf.finalize_conf() +#conf.set_options_from_testing_section() + from webadmin import purge_users from webadmin import purge_resources from webadmin import purge_shared_folders from webadmin import get_user from webadmin import add_user from webadmin import get_resource from webadmin import add_resource from webadmin import get_shared_folder from webadmin import add_shared_folder from imap import purge_imap from ldap import synchronize def wipeall(): log = pykolab.getLogger('stick') log.info("Wiping all data, yeah!") purge_users() purge_resources() purge_shared_folders() time.sleep(2) purge_imap() time.sleep(2) log.info("Done") + from seleniumtest import KolabSeleniumTest from integrationtest import KolabIntegrationTest from phpunittest import KolabPhpunitTest - +from testloader import KolabTestLoader diff --git a/integration/stick/integrationtest.py b/integration/stick/integrationtest.py index 018da03..ede018f 100644 --- a/integration/stick/integrationtest.py +++ b/integration/stick/integrationtest.py @@ -1,163 +1,161 @@ import unittest import pykolab import datetime import time from . import wipeall from . import synchronize from . import get_user from . import add_user from . import get_resource from . import add_resource from . import get_shared_folder from . import add_shared_folder from selenium import webdriver conf = pykolab.getConf() -conf.finalize_conf() -#conf.set_options_from_testing_section() class KolabIntegrationTest(unittest.TestCase): """ Base class providing utility functions for running integration tests """ wiped = False dosync = False initialized = False develmode = conf.get('testing', 'develmode') == 'true' verbose = conf.get('testing', 'verbose') == 'true' @classmethod def setUp(self, *args, **kw): """ Trigger setUpClass() once """ if not self.initialized: self.setUpClass() @classmethod def setUpClass(self, *args, **kw): """ Call this at the end of the derived setUpClass() method """ self.initialized = True if self.dosync: time.sleep(1) self.log("Synchronize...") synchronize() self.dosync = False @classmethod def tearDownClass(self): if not self.develmode: self.log("Wipe data after") wipeall() @classmethod def log(self, message): if self.verbose: print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:23] + " " + message @classmethod def require_user(self, givenname, sn, **kw): """ Create the given user and return its full properties """ create = not self.initialized and not self.develmode res = { 'givenname': givenname, 'sn': sn } res.update(kw) if not create: query = get_user(givenname, sn) self.log("User found: %r" % (query.get('mail','') if isinstance(query, dict) else query)) if len(query) > 0: res = query res['userpassword'] = 'Welcome2KolabSystems' else: create = True else: self._wipefirst() if create: res = add_user(givenname, sn, **kw) self.log("Added user: %r" % (res.get('mail',''))) self.dosync = True return res @classmethod def require_resource(self, type, cn, members=None, **kw): """ Create the given resource and return its properties """ create = not self.initialized and not self.develmode res = { 'type': type, 'cn': cn, 'members': members } res.update(kw) if not create: query = get_resource(type, cn) self.log("Resource found: %r" % (query.get('cn','') if isinstance(query, dict) else query)) if len(query) > 0: res = query else: create = True else: self._wipefirst() if create: res = add_resource(type, cn, members, **kw) self.log("Added resource: %r" % (res.get('cn',''))) self.dosync = True return res @classmethod def require_shared_folder(self, type, cn, **kw): """ Create the given shared folder and return its properties """ create = not self.initialized and not self.develmode res = { 'kolabfoldertype': type, 'cn': cn } res.update(kw) if not create: query = get_shared_folder(type, cn) self.log("Folder found: %r" % (query.get('cn','') if isinstance(query, dict) else query)) if len(query) > 0: res = query else: create = True else: self._wipefirst() if create: res = add_shared_folder(type, cn, **kw) self.log("Added shared folder: %r" % (res.get('cn',''))) self.dosync = True return res @classmethod def _wipefirst(self): """ Helper method to remove all data from the Kolab backend """ if not self.wiped: self.log("Wipe data first") wipeall() self.wiped = True def assertIn(self, needle, haystack, message=None): self.assertTrue(needle in haystack, message) def get_conf(self, section, key, default=None): return conf.get(section, key) or default diff --git a/integration/stick/phpunittest.py b/integration/stick/phpunittest.py index 4b56f4b..419b3a6 100644 --- a/integration/stick/phpunittest.py +++ b/integration/stick/phpunittest.py @@ -1,110 +1,115 @@ import time import subprocess from integrationtest import KolabIntegrationTest class KolabPhpunitTest(KolabIntegrationTest): """ Base class for running Roundcube Selenium tests through phpunit This assumes Roundcube being installed with dev dependencies that include the phpunit/phpunit-selenium package. """ server = None - env = { 'ROUNDCUBE_TEST_BROWSER': 'phantomjs' } + env = {} @classmethod def setUpClass(self, *args, **kw): KolabIntegrationTest.setUpClass(self) def setUp(self, *args, **kw): if not self.initialized: self.__class__.setUpClass(self.__class__, *args, **kw) - self._start_server() if hasattr(self, 'installHandler'): self.installHandler() def tearDown(self, *args, **kw): self._stop_server() KolabIntegrationTest.tearDown(self, *args, **kw) def stop(self): self._stop_server() def _start_server(self): # only start once if self.server is None: server_jar = self.get_conf('testing', 'selenium_server_jar', 'selenium-server-standalone-2.45.0.jar') self.log("Starting Selenium server %r" % (server_jar)) self.server = subprocess.Popen(['java', '-jar', server_jar], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) ready = False while not ready and not self.server.stdout.closed and self.server.poll() is None: line = self.server.stdout.readline() self.log("* " + line.strip()) if line is None: break elif "Started SocketListener" in line: ready = True elif "Failed" in line or "Error" in line: break if ready: self.log("Selenium server started.") else: raise Exception("Failed to start Selenium server") + self.env['ROUNDCUBE_TEST_BROWSER'] = 'phantomjs' + def _stop_server(self): if self.server: self.server.terminate() self.server = None self.log("Selenium server stopped") def set_env(self, var, value): """ Set Roundcube config variable through env variables """ self.env['ROUNDCUBE_' + var.upper()] = value def set_login(self, username, password): """ Set Roundcube login username and password for subsequent phpunit runs """ self.set_env('tests_username', username) self.set_env('tests_password', password) - def execute(self, testfile=None, testsuite=None, testfilter=None, message=None): + def execute(self, testfile=None, testsuite=None, testfilter=None, selenium=False, message=None): """ Execute phpunit on a Roundcube test instance """ cwd = self.get_conf('testing', 'roundcube_dir', '/usr/share/roundcubemail') pbin = self.get_conf('testing', 'phpunit_bin', 'vendor/bin/phpunit') - args = [ pbin, '--configuration', 'tests/Selenium/phpunit.xml' ] + conf = 'tests/Selenium/phpunit.xml' if selenium else 'tests/phpunit.xml' + args = [ pbin, '--configuration', conf ] if self.verbose: args.append('--verbose') if testfile: args.append(testfile) else: if testsuite: args.append('--testsuite') args.append(testsuite) if testfilter: args.append('--filter') args.append(testfilter) + if selenium: + self._start_server() + self.log("Executing %r with %r" % (args, self.env)) p = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=self.env) for line in iter(p.stdout.readline, ''): self.log(line.strip()) retval = p.wait() self.assertEqual(retval, 0, message) diff --git a/integration/stick/testloader.py b/integration/stick/testloader.py new file mode 100644 index 0000000..e5894cb --- /dev/null +++ b/integration/stick/testloader.py @@ -0,0 +1,17 @@ +import unittest + +class KolabTestLoader(unittest.TestLoader): + + def __init__(self, opts, *args, **kw): + self.opts = opts + unittest.TestLoader.__init__(self, *args, **kw) + + def loadTestsFromTestCase(self, testCase): + # check TODO flag of the test case class + if not hasattr(testCase, 'TODO') or self.opts.todo == bool(testCase.TODO): + tests = unittest.TestLoader.loadTestsFromTestCase(self, testCase) + else: + tests = unittest.TestSuite() + + return tests +