zeitgeist team mailing list archive
-
zeitgeist team
-
Mailing list archive
-
Message #01896
lp:~thekorn/zeitgeist/fix-634740-634744-testrunner-improvements into lp:zeitgeist
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.kamstrup@xxxxxxxxx>
+# Copyright © 2009-2010 Markus Korn <thekorn@xxxxxx>
#
# 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
Follow ups