Markus Korn has proposed merging lp:~thekorn/zeitgeist/fix-634740-634744-testrunner-improvements into lp:zeitgeist.
Requested reviews: Zeitgeist Framework Team (zeitgeist) Related bugs: #634740 explicitly define on a per testcase basis which extension needs to be loaded https://bugs.launchpad.net/bugs/634740 #634744 test suite fails if zeitgeist is installed on the system https://bugs.launchpad.net/bugs/634744 * Make sure to run all tests using its own temporary ZEITGEIST_DATA_PATH, ZEITGEIST_DATABASE set to ":memory:", and as much isolated from other tests as possible. Unfortunately this requires some lazy imports (LP: #634740) * `make check` (or `test/run-all-tests.py`) runs all tests now on a private DBUs bus (LP: #634744) -- https://code.launchpad.net/~thekorn/zeitgeist/fix-634740-634744-testrunner-improvements/+merge/36134 Your team Zeitgeist Framework Team is requested to review the proposed merge of lp:~thekorn/zeitgeist/fix-634740-634744-testrunner-improvements into lp:zeitgeist.
=== modified file 'test/engine-extension-test.py' --- test/engine-extension-test.py 2010-08-02 10:13:12 +0000 +++ test/engine-extension-test.py 2010-09-21 11:46:43 +0000 @@ -7,30 +7,23 @@ import weakref sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -import _zeitgeist.engine -from _zeitgeist.engine import constants -from _zeitgeist.engine import get_engine -from _zeitgeist.engine.extension import Extension - import unittest from testutils import import_events -class _Extension1(Extension): - PUBLIC_METHODS = ["return_hallo", "return_engine"] - - def return_hallo(self): - return "Hallo" - - def return_boo(self): - return "boo" - - def return_engine(self): - return self.engine - +Extension = None class _engineTestClass(unittest.TestCase): def setUp (self): + global Extension + + from _zeitgeist.engine import constants + from _zeitgeist.engine import get_engine + + if Extension is None: + from _zeitgeist.engine.extension import Extension as _Extension + Extension = _Extension + constants.DATABASE_FILE = ":memory:" self.save_default_ext = os.environ.get("ZEITGEIST_DEFAULT_EXTENSIONS") self.save_extra_ext = os.environ.get("ZEITGEIST_EXTRA_EXTENSIONS") @@ -39,6 +32,7 @@ self.engine = get_engine() def tearDown (self): + import _zeitgeist.engine if self.save_default_ext is not None: os.environ["ZEITGEIST_DEFAULT_EXTENSIONS"] = self.save_default_ext else: @@ -54,13 +48,27 @@ class TestExtensions(_engineTestClass): def testCreateEngine(self): - engine = get_engine() + + class _Extension1(Extension): + PUBLIC_METHODS = ["return_hallo", "return_engine"] + + def return_hallo(self): + return "Hallo" + + def return_boo(self): + return "boo" + + def return_engine(self): + return self.engine + + engine = self.engine self.assertEqual(len(engine.extensions), 0) self.assertRaises(AttributeError, engine.extensions.__getattr__, "return_hallo") engine.extensions.load(_Extension1) self.assertEqual(engine.extensions.return_hallo(), "Hallo") self.assertRaises(AttributeError, engine.extensions.__getattr__, "return_boo") self.assertEqual(engine.extensions.return_engine(), weakref.proxy(engine)) + class TestExtensionHooks(_engineTestClass): === modified file 'test/engine-test.py' --- test/engine-test.py 2010-08-31 20:48:33 +0000 +++ test/engine-test.py 2010-09-21 11:46:43 +0000 @@ -6,9 +6,6 @@ import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -import _zeitgeist.engine -from _zeitgeist.engine import constants -from _zeitgeist.engine import get_engine from zeitgeist.datamodel import * from testutils import import_events @@ -40,6 +37,9 @@ class _engineTestClass(unittest.TestCase): def setUp (self): + from _zeitgeist.engine import constants + from _zeitgeist.engine import get_engine + self.save_default_ext = os.environ.get("ZEITGEIST_DEFAULT_EXTENSIONS") self.save_extra_ext = os.environ.get("ZEITGEIST_EXTRA_EXTENSIONS") os.environ["ZEITGEIST_DEFAULT_EXTENSIONS"] = "" @@ -57,6 +57,7 @@ self.engine = get_engine() def tearDown (self): + import _zeitgeist.engine if self.save_default_ext is not None: os.environ["ZEITGEIST_DEFAULT_EXTENSIONS"] = self.save_default_ext else: === modified file 'test/loggers-datasources-recent-test.py' --- test/loggers-datasources-recent-test.py 2009-11-30 07:57:58 +0000 +++ test/loggers-datasources-recent-test.py 2010-09-21 11:46:43 +0000 @@ -7,16 +7,28 @@ import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from _zeitgeist.loggers.datasources.recent import SimpleMatch, MimeTypeSet - -class SimpleMatchTest(unittest.TestCase): + +SimpleMatch = None +MimeTypeSet = None + +class BaseTestCase(unittest.TestCase): + + def setUp(self): + global SimpleMatch + global MimeTypeSet + if None in (SimpleMatch, MimeTypeSet): + from _zeitgeist.loggers.datasources.recent import SimpleMatch as _SM, MimeTypeSet as _MTS + SimpleMatch = _SM + MimeTypeSet = _MTS + +class SimpleMatchTest(BaseTestCase): def testmatch(self): self.assertTrue(SimpleMatch("boo/*").match("boo/bar")) self.assertTrue(SimpleMatch("boo/bar.*").match("boo/bar.foo")) self.assertFalse(SimpleMatch("boo/bar.*").match("boo/barfoo")) -class MimeTypeSetTest(unittest.TestCase): +class MimeTypeSetTest(BaseTestCase): def testinit(self): self.assertEquals(repr(MimeTypeSet("boo", "bar", "foo")), "MimeTypeSet('bar', 'boo', 'foo')") === modified file 'test/remote-test.py' --- test/remote-test.py 2010-09-15 14:20:21 +0000 +++ test/remote-test.py 2010-09-21 11:46:43 +0000 @@ -6,6 +6,8 @@ import logging import signal import time +import tempfile +import shutil from subprocess import Popen, PIPE # DBus setup @@ -18,7 +20,6 @@ from zeitgeist.client import ZeitgeistDBusInterface, ZeitgeistClient from zeitgeist.datamodel import (Event, Subject, Interpretation, Manifestation, TimeRange, StorageState) -from _zeitgeist.engine.remote import RemoteInterface import testutils from testutils import parse_events @@ -322,9 +323,31 @@ class ZeitgeistRemoteInterfaceTest(unittest.TestCase): + def setUp(self): + from _zeitgeist import engine + from _zeitgeist.engine import sql, constants + engine._engine = None + sql.unset_cursor() + self.saved_data = { + "datapath": constants.DATA_PATH, + "database": constants.DATABASE_FILE, + "extensions": constants.USER_EXTENSION_PATH, + } + constants.DATA_PATH = tempfile.mkdtemp(prefix="zeitgeist.datapath.") + constants.DATABASE_FILE = ":memory:" + constants.USER_EXTENSION_PATH = os.path.join(constants.DATA_PATH, "extensions") + + def tearDown(self): + from _zeitgeist.engine import constants + shutil.rmtree(constants.DATA_PATH) + constants.DATA_PATH = self.saved_data["datapath"] + constants.DATABASE_FILE = self.saved_data["database"] + constants.USER_EXTENSION_PATH = self.saved_data["extensions"] + def testQuit(self): """calling Quit() on the remote interface should shutdown the engine in a clean way""" + from _zeitgeist.engine.remote import RemoteInterface interface = RemoteInterface() self.assertEquals(interface._engine.is_closed(), False) interface.Quit() @@ -332,14 +355,25 @@ class ZeitgeistDaemonTest(unittest.TestCase): + def setUp(self): + self.env = os.environ.copy() + self.datapath = tempfile.mkdtemp(prefix="zeitgeist.datapath.") + self.env.update({ + "ZEITGEIST_DATABASE_PATH": ":memory:", + "ZEITGEIST_DATA_PATH": self.datapath, + }) + + def tearDown(self): + shutil.rmtree(self.datapath) + def testSIGHUP(self): """sending a SIGHUP signal to a running deamon instance results in a clean shutdown""" daemon = Popen( - ["./zeitgeist-daemon.py", "--no-datahub"], stderr=PIPE, stdout=PIPE + ["./zeitgeist-daemon.py", "--no-datahub"], stderr=PIPE, stdout=PIPE, env=self.env ) # give the daemon some time to wake up - time.sleep(3) + time.sleep(1) err = daemon.poll() if err: raise RuntimeError("Could not start daemon, got err=%i" % err) === modified file 'test/run-all-tests.py' --- test/run-all-tests.py 2010-09-02 14:33:04 +0000 +++ test/run-all-tests.py 2010-09-21 11:46:43 +0000 @@ -6,36 +6,67 @@ import doctest import logging import sys +import tempfile +import shutil from optparse import OptionParser -parser = OptionParser() -parser.add_option("-v", action="count", dest="verbosity") -(options, args) = parser.parse_args() - -if options.verbosity: - # do more fine grained stuff later - # redirect all debugging output to stderr - logging.basicConfig(stream=sys.stderr) -else: - logging.basicConfig(filename="/dev/null") sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) # Find the test/ directory -testdir = os.path.dirname(os.path.abspath(__file__)) -doctests = glob.glob(os.path.join(testdir, "*.rst")) - -# Create a test suite to run all tests -# first, add all doctests -arguments = {"module_relative": False, "globs": {"sys": sys}} -suite = doctest.DocFileSuite(*doctests, **arguments) - -# Add all of the tests from each file that ends with "-test.py" -for fname in os.listdir(testdir): - if fname.endswith("-test.py"): - fname = os.path.basename(fname)[:-3] # Get the filename and chop off ".py" - module = __import__(fname) - suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(module)) - -# Run all of the tests -unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite) +TESTDIR = os.path.dirname(os.path.abspath(__file__)) +DOCTESTS = glob.glob(os.path.join(TESTDIR, "*.rst")) + +def doctest_setup(test): + test._datapath = tempfile.mkdtemp(prefix="zeitgeist.datapath.") + test._env = os.environ.copy() + os.environ.update({ + "ZEITGEIST_DATABASE_PATH": ":memory:", + "ZEITGEIST_DATA_PATH": test._datapath + }) + +def doctest_teardown(test): + shutil.rmtree(test._datapath) + os.environ = test._env + +def compile_suite(): + # Create a test suite to run all tests + + # first, add all doctests + arguments = { + "module_relative": False, + "globs": {"sys": sys}, + "setUp": doctest_setup, + "tearDown": doctest_teardown, + } + suite = doctest.DocFileSuite(*DOCTESTS, **arguments) + + # Add all of the tests from each file that ends with "-test.py" + for fname in os.listdir(TESTDIR): + if fname.endswith("-test.py"): + fname = os.path.basename(fname)[:-3] # Get the filename and chop off ".py" + module = __import__(fname) + suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(module)) + return suite + +if __name__ == "__main__": + parser = OptionParser() + parser.add_option("-v", action="count", dest="verbosity") + (options, args) = parser.parse_args() + + if options.verbosity: + # do more fine grained stuff later + # redirect all debugging output to stderr + logging.basicConfig(stream=sys.stderr) + else: + logging.basicConfig(filename="/dev/null") + + from testutils import DBusPrivateMessageBus + bus = DBusPrivateMessageBus() + bus.run() + try: + suite = compile_suite() + # Run all of the tests + unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite) + finally: + bus.quit() === modified file 'test/sql-test.py' --- test/sql-test.py 2010-09-19 10:42:30 +0000 +++ test/sql-test.py 2010-09-21 11:46:43 +0000 @@ -23,10 +23,16 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) import unittest -from _zeitgeist.engine.sql import * +WhereClause = None class SQLTest (unittest.TestCase): + def setUp(self): + global WhereClause + if WhereClause is None: + from _zeitgeist.engine.sql import WhereClause as _WhereClause + WhereClause = _WhereClause + def testFlat (self): where = WhereClause(WhereClause.AND) where.add ("foo = %s", 10) === modified file 'test/testutils.py' --- test/testutils.py 2010-07-22 09:52:53 +0000 +++ test/testutils.py 2010-09-21 11:46:43 +0000 @@ -3,6 +3,7 @@ # Zeitgeist # # Copyright © 2009 Mikkel Kamstrup Erlandsen <mikkel.kamst...@gmail.com> +# Copyright © 2009-2010 Markus Korn <thek...@gmx.de> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by @@ -22,6 +23,8 @@ import time import sys import signal +import tempfile +import shutil from subprocess import Popen, PIPE # DBus setup @@ -41,8 +44,6 @@ # maybe the user is using python < 2.6 import simplejson as json -from zeitgeist.datamodel import Event, Subject - def dict2event(d): ev = Event() ev[0][Event.Id] = d.get("id", "").encode("UTF-8") @@ -92,12 +93,11 @@ self.client = None def spawn_daemon(self): - os.environ.update({"ZEITGEIST_DATABASE_PATH": ":memory:"}) self.daemon = Popen( - ["./zeitgeist-daemon.py", "--no-datahub"], stderr=sys.stderr, stdout=sys.stderr + ["./zeitgeist-daemon.py", "--no-datahub"], stderr=sys.stderr, stdout=sys.stderr, env=self.env ) # give the daemon some time to wake up - time.sleep(3) + time.sleep(1) err = self.daemon.poll() if err: raise RuntimeError("Could not start daemon, got err=%i" % err) @@ -109,6 +109,12 @@ def setUp(self): assert self.daemon is None assert self.client is None + self.env = os.environ.copy() + self.datapath = tempfile.mkdtemp(prefix="zeitgeist.datapath.") + self.env.update({ + "ZEITGEIST_DATABASE_PATH": ":memory:", + "ZEITGEIST_DATA_PATH": self.datapath, + }) self.spawn_daemon() # hack to clear the state of the interface @@ -119,6 +125,7 @@ assert self.daemon is not None assert self.client is not None self.kill_daemon() + shutil.rmtree(self.datapath) def insertEventsAndWait(self, events): """ @@ -220,3 +227,29 @@ num_events=num_events, result_type=result_type) mainloop.run() return result + +class DBusPrivateMessageBus(object): + DISPLAY = ":27" + + def run(self): + os.environ.update({"DISPLAY": self.DISPLAY}) + self.display = Popen(["Xvfb", self.DISPLAY, "-screen", "0", "1024x768x8"]) + # give the display some time to wake up + time.sleep(1) + err = self.display.poll() + if err: + raise RuntimeError("Could not start Xvfb on display %s, got err=%i" %(self.DISPLAY, err)) + dbus = Popen(["dbus-launch"], stdout=PIPE) + time.sleep(1) + self.dbus_config = dict(l.split("=", 1) for l in dbus.communicate()[0].split("\n") if l) + os.environ.update(self.dbus_config) + + def quit(self): + os.kill(self.display.pid, signal.SIGKILL) + self.display.wait() + pid = int(self.dbus_config["DBUS_SESSION_BUS_PID"]) + os.kill(pid, signal.SIGKILL) + try: + os.waitpid(pid, 0) + except OSError: + pass
_______________________________________________ Mailing list: https://launchpad.net/~zeitgeist Post to : zeitgeist@lists.launchpad.net Unsubscribe : https://launchpad.net/~zeitgeist More help : https://help.launchpad.net/ListHelp