Merge lp:~thekorn/zeitgeist/fix-634740-634744-testrunner-improvements into lp:zeitgeist/0.1

Proposed by Markus Korn
Status: Merged
Merged at revision: 1602
Proposed branch: lp:~thekorn/zeitgeist/fix-634740-634744-testrunner-improvements
Merge into: lp:zeitgeist/0.1
Diff against target: 607 lines (+293/-76)
9 files modified
bug_testrunner.py (+10/-0)
test/blacklist-test.py (+3/-1)
test/engine-extension-test.py (+26/-18)
test/engine-test.py (+6/-4)
test/loggers-datasources-recent-test.py (+16/-4)
test/remote-test.py (+36/-9)
test/run-all-tests.py (+98/-27)
test/sql-test.py (+7/-1)
test/testutils.py (+91/-12)
To merge this branch: bzr merge lp:~thekorn/zeitgeist/fix-634740-634744-testrunner-improvements
Reviewer Review Type Date Requested Status
Siegfried Gevatter Approve
Seif Lotfy Needs Fixing
Review via email: mp+36134@code.launchpad.net

Description of the change

* 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)

To post a comment you must log in.
1599. By Markus Korn

merged recent changes from lp:zeitgeist

Revision history for this message
Seif Lotfy (seif) wrote :

It still fails with the blacklist-test.py! I had to quit zeitgeist manually for the tests to succeed. Here is my print out.

seif@Wumbo:~/Projects/xxx/zeitgeist$ test/blacklist-test.py
EEEE
======================================================================
ERROR: testApplyBlacklist (__main__.BlacklistTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/blacklist-test.py", line 24, in setUp
    super(BlacklistTest, self).setUp()
  File "/home/seif/Projects/xxx/zeitgeist/test/testutils.py", line 118, in setUp
    self.spawn_daemon()
  File "/home/seif/Projects/xxx/zeitgeist/test/testutils.py", line 103, in spawn_daemon
    raise RuntimeError("Could not start daemon, got err=%i" % err)
RuntimeError: Could not start daemon, got err=1

======================================================================
ERROR: testBlacklistUsingClientDBusInterface (__main__.BlacklistTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/blacklist-test.py", line 24, in setUp
    super(BlacklistTest, self).setUp()
  File "/home/seif/Projects/xxx/zeitgeist/test/testutils.py", line 118, in setUp
    self.spawn_daemon()
  File "/home/seif/Projects/xxx/zeitgeist/test/testutils.py", line 103, in spawn_daemon
    raise RuntimeError("Could not start daemon, got err=%i" % err)
RuntimeError: Could not start daemon, got err=1

======================================================================
ERROR: testClear (__main__.BlacklistTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/blacklist-test.py", line 24, in setUp
    super(BlacklistTest, self).setUp()
  File "/home/seif/Projects/xxx/zeitgeist/test/testutils.py", line 118, in setUp
    self.spawn_daemon()
  File "/home/seif/Projects/xxx/zeitgeist/test/testutils.py", line 103, in spawn_daemon
    raise RuntimeError("Could not start daemon, got err=%i" % err)
RuntimeError: Could not start daemon, got err=1

======================================================================
ERROR: testSetOne (__main__.BlacklistTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/blacklist-test.py", line 24, in setUp
    super(BlacklistTest, self).setUp()
  File "/home/seif/Projects/xxx/zeitgeist/test/testutils.py", line 118, in setUp
    self.spawn_daemon()
  File "/home/seif/Projects/xxx/zeitgeist/test/testutils.py", line 103, in spawn_daemon
    raise RuntimeError("Could not start daemon, got err=%i" % err)
RuntimeError: Could not start daemon, got err=1

----------------------------------------------------------------------
Ran 4 tests in 4.014s

FAILED (errors=4)

Revision history for this message
Seif Lotfy (seif) :
review: Needs Fixing
Revision history for this message
Seif Lotfy (seif) wrote :
Download full text (12.4 KiB)

Here is the full output when running test/run-all-tests.py
-----------------------------------------------------------

seif@Wumbo:~/Projects/xxx/zeitgeist$ test/run-all-tests.py
[dix] Could not init font path element /usr/share/fonts/X11/cyrillic, removing from list!
5 XSELINUXs still allocated at reset
SCREEN: 0 objects of 68 bytes = 0 total bytes 0 private allocs
DEVICE: 4 objects of 16 bytes = 64 total bytes 0 private allocs
CLIENT: 0 objects of 152 bytes = 0 total bytes 0 private allocs
WINDOW: 0 objects of 16 bytes = 0 total bytes 0 private allocs
PIXMAP: 1 objects of 8 bytes = 8 total bytes 0 private allocs
GC: 0 objects of 44 bytes = 0 total bytes 0 private allocs
CURSOR: 0 objects of 4 bytes = 0 total bytes 0 private allocs
CURSOR_BITS: 0 objects of 4 bytes = 0 total bytes 0 private allocs
DBE_WINDOW: 0 objects of 12 bytes = 0 total bytes 0 private allocs
TOTAL: 5 objects, 72 bytes, 0 allocs
4 DEVICEs still allocated at reset
DEVICE: 4 objects of 16 bytes = 64 total bytes 0 private allocs
CLIENT: 0 objects of 152 bytes = 0 total bytes 0 private allocs
WINDOW: 0 objects of 16 bytes = 0 total bytes 0 private allocs
PIXMAP: 1 objects of 8 bytes = 8 total bytes 0 private allocs
GC: 0 objects of 44 bytes = 0 total bytes 0 private allocs
CURSOR: 0 objects of 4 bytes = 0 total bytes 0 private allocs
CURSOR_BITS: 0 objects of 4 bytes = 0 total bytes 0 private allocs
DBE_WINDOW: 0 objects of 12 bytes = 0 total bytes 0 private allocs
TOTAL: 5 objects, 72 bytes, 0 allocs
1 PIXMAPs still allocated at reset
PIXMAP: 1 objects of 8 bytes = 8 total bytes 0 private allocs
GC: 0 objects of 44 bytes = 0 total bytes 0 private allocs
CURSOR: 0 objects of 4 bytes = 0 total bytes 0 private allocs
CURSOR_BITS: 0 objects of 4 bytes = 0 total bytes 0 private allocs
DBE_WINDOW: 0 objects of 12 bytes = 0 total bytes 0 private allocs
TOTAL: 1 objects, 8 bytes, 0 allocs
[dix] Could not init font path element /usr/share/fonts/X11/cyrillic, removing from list!
Doctest: test-engine-extension.rst ... ok
test_mime_none (mimetypes-test.MimetypesTest) ... ok
test_mime_regex (mimetypes-test.MimetypesTest) ... ok
test_textplain (mimetypes-test.MimetypesTest) ... ok
test_scheme_file (mimetypes-test.SchemeTest) ... ok
test_scheme_none (mimetypes-test.SchemeTest) ... ok
testDeleteHook (engine-extension-test.TestExtensionHooks) ... ok
testGetHook (engine-extension-test.TestExtensionHooks) ... ok
testInsertHook (engine-extension-test.TestExtensionHooks) ... ok
testCreateEngine (engine-extension-test.TestExtensions) ... ok
for now we raise a ValueError if someone wants to search ... ok
testInTimeRange (datamodel-test.EventTest) ... ok
testNegationCombination (datamodel-test.EventTest) ... ok
testNegationFields (datamodel-test.EventTest) ... ok
testNegationTemplateMatching (datamodel-test.EventTest) ... ok
testNegationWildcardTemplateMatching (datamodel-test.EventTest) ... ok
testNewForValues1 (datamodel-test.EventTest) ... ok
testNewForValues2 (datamodel-test.EventTest) ... ok
testSimple (datamodel-test.EventTest) ... ok
testTemplateFiltering (datamodel-test.EventTest) ... ok
testTemplateMatching (datamodel-test.EventTest) ... ok
testTemplateParentMatching (datamodel-test.Ev...

Revision history for this message
Markus Korn (thekorn) wrote :

> It still fails with the blacklist-test.py! I had to quit zeitgeist manually
> for the tests to succeed. Here is my print out.
>
>
> seif@Wumbo:~/Projects/xxx/zeitgeist$ test/blacklist-test.py
> EEEE
[...]

Please note, the private/temporary DBus bus only works if you run the whole testsuite, by running `make check` or `test/run-all-tests.py`
I did not want to fire-up one temporary bus for each testcase, because I think this operation is too expensive (time consuming). And unfortunatly python2.5's unittest module does not have a way for module-wide setUp/tearDown methods.

1600. By Markus Korn

merged recent changes from lp:zeitgeist and resolved conflicts

1601. By Markus Korn

replaced spaces by tabs

1602. By Markus Korn

* print some information to stderr about the running private dbus bus
* add 'ignore_errors' option to DBusPrivateMessageBus.{run(),quit()}
  failures in these methods should never cause the actual tests to fail,
  as a fallback the old way (using the users session bus) will be used

1603. By Markus Korn

be more verbose in RuntimeError in case the zeitgeist-daemon fails to start in the testsuite

1604. By Markus Korn

* try to be more verbose in the testcases in case of zeitgeist-daemon
  failures

1605. By Markus Korn

added missing lazy import

1606. By Markus Korn

be even more verbose

1607. By Markus Korn

* test/run-all-tests.py: it is now possible to select which tests to run,
  that's a first step to clean up the testsuite

1608. By Markus Korn

small fix to make all tests runable

1609. By Markus Korn

also filter doctests based on pattern

1610. By Markus Korn

added a test script for seif's issue

1611. By Markus Korn

added some debug statement

Revision history for this message
Siegfried Gevatter (rainct) wrote :

Merged, good work.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'bug_testrunner.py'
2--- bug_testrunner.py 1970-01-01 00:00:00 +0000
3+++ bug_testrunner.py 2010-09-23 11:07:07 +0000
4@@ -0,0 +1,10 @@
5+import os
6+import glob
7+from subprocess import Popen
8+
9+tests = map(lambda x: os.path.basename(x).rsplit(".", 1)[0], glob.glob("test/*.rst"))
10+tests += map(lambda x: os.path.basename(x).rsplit(".", 1)[0], glob.glob("test/*-test.py"))
11+
12+for test in tests:
13+ print "*****************", test
14+ Popen(["test/run-all-tests.py", test, "blacklist-test"]).wait()
15
16=== modified file 'test/blacklist-test.py'
17--- test/blacklist-test.py 2010-04-26 19:42:07 +0000
18+++ test/blacklist-test.py 2010-09-23 11:07:07 +0000
19@@ -5,7 +5,6 @@
20 import sys
21 import os
22 import unittest
23-import dbus
24
25 sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
26 from zeitgeist.client import ZeitgeistDBusInterface
27@@ -19,6 +18,9 @@
28 self.blacklist = None
29
30 def setUp(self):
31+ # lazy import to get a chance to use the private bus
32+ import dbus
33+
34 # We set up the connection lazily in order to wait for the
35 # engine to come up
36 super(BlacklistTest, self).setUp()
37
38=== modified file 'test/engine-extension-test.py'
39--- test/engine-extension-test.py 2010-08-02 10:13:12 +0000
40+++ test/engine-extension-test.py 2010-09-23 11:07:07 +0000
41@@ -7,30 +7,23 @@
42 import weakref
43 sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
44
45-import _zeitgeist.engine
46-from _zeitgeist.engine import constants
47-from _zeitgeist.engine import get_engine
48-from _zeitgeist.engine.extension import Extension
49-
50 import unittest
51 from testutils import import_events
52
53-class _Extension1(Extension):
54- PUBLIC_METHODS = ["return_hallo", "return_engine"]
55-
56- def return_hallo(self):
57- return "Hallo"
58-
59- def return_boo(self):
60- return "boo"
61-
62- def return_engine(self):
63- return self.engine
64-
65+Extension = None
66
67 class _engineTestClass(unittest.TestCase):
68
69 def setUp (self):
70+ global Extension
71+
72+ from _zeitgeist.engine import constants
73+ from _zeitgeist.engine import get_engine
74+
75+ if Extension is None:
76+ from _zeitgeist.engine.extension import Extension as _Extension
77+ Extension = _Extension
78+
79 constants.DATABASE_FILE = ":memory:"
80 self.save_default_ext = os.environ.get("ZEITGEIST_DEFAULT_EXTENSIONS")
81 self.save_extra_ext = os.environ.get("ZEITGEIST_EXTRA_EXTENSIONS")
82@@ -39,6 +32,7 @@
83 self.engine = get_engine()
84
85 def tearDown (self):
86+ import _zeitgeist.engine
87 if self.save_default_ext is not None:
88 os.environ["ZEITGEIST_DEFAULT_EXTENSIONS"] = self.save_default_ext
89 else:
90@@ -54,13 +48,27 @@
91 class TestExtensions(_engineTestClass):
92
93 def testCreateEngine(self):
94- engine = get_engine()
95+
96+ class _Extension1(Extension):
97+ PUBLIC_METHODS = ["return_hallo", "return_engine"]
98+
99+ def return_hallo(self):
100+ return "Hallo"
101+
102+ def return_boo(self):
103+ return "boo"
104+
105+ def return_engine(self):
106+ return self.engine
107+
108+ engine = self.engine
109 self.assertEqual(len(engine.extensions), 0)
110 self.assertRaises(AttributeError, engine.extensions.__getattr__, "return_hallo")
111 engine.extensions.load(_Extension1)
112 self.assertEqual(engine.extensions.return_hallo(), "Hallo")
113 self.assertRaises(AttributeError, engine.extensions.__getattr__, "return_boo")
114 self.assertEqual(engine.extensions.return_engine(), weakref.proxy(engine))
115+
116
117 class TestExtensionHooks(_engineTestClass):
118
119
120=== modified file 'test/engine-test.py'
121--- test/engine-test.py 2010-09-19 09:24:40 +0000
122+++ test/engine-test.py 2010-09-23 11:07:07 +0000
123@@ -6,10 +6,6 @@
124 import os
125 sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
126
127-import _zeitgeist.engine
128-from _zeitgeist.engine import constants
129-from _zeitgeist.engine import get_engine
130-from _zeitgeist.engine.sql import WhereClause
131 from zeitgeist.datamodel import *
132 from testutils import import_events
133
134@@ -41,6 +37,9 @@
135 class _engineTestClass(unittest.TestCase):
136
137 def setUp (self):
138+ from _zeitgeist.engine import constants
139+ from _zeitgeist.engine import get_engine
140+
141 self.save_default_ext = os.environ.get("ZEITGEIST_DEFAULT_EXTENSIONS")
142 self.save_extra_ext = os.environ.get("ZEITGEIST_EXTRA_EXTENSIONS")
143 os.environ["ZEITGEIST_DEFAULT_EXTENSIONS"] = ""
144@@ -58,6 +57,7 @@
145 self.engine = get_engine()
146
147 def tearDown (self):
148+ import _zeitgeist.engine
149 if self.save_default_ext is not None:
150 os.environ["ZEITGEIST_DEFAULT_EXTENSIONS"] = self.save_default_ext
151 else:
152@@ -935,6 +935,8 @@
153 self.assertEquals(5, len(ids))
154
155 def testWildcardOptimization(self):
156+ from _zeitgeist.engine.sql import WhereClause
157+
158 cursor = self.engine._cursor
159 strings = [
160 (u"hällö, I'm gürmen - åge drikker øl - ☠ bug",),
161
162=== modified file 'test/loggers-datasources-recent-test.py'
163--- test/loggers-datasources-recent-test.py 2009-11-30 07:57:58 +0000
164+++ test/loggers-datasources-recent-test.py 2010-09-23 11:07:07 +0000
165@@ -7,16 +7,28 @@
166 import unittest
167
168 sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
169-from _zeitgeist.loggers.datasources.recent import SimpleMatch, MimeTypeSet
170-
171-class SimpleMatchTest(unittest.TestCase):
172+
173+SimpleMatch = None
174+MimeTypeSet = None
175+
176+class BaseTestCase(unittest.TestCase):
177+
178+ def setUp(self):
179+ global SimpleMatch
180+ global MimeTypeSet
181+ if None in (SimpleMatch, MimeTypeSet):
182+ from _zeitgeist.loggers.datasources.recent import SimpleMatch as _SM, MimeTypeSet as _MTS
183+ SimpleMatch = _SM
184+ MimeTypeSet = _MTS
185+
186+class SimpleMatchTest(BaseTestCase):
187
188 def testmatch(self):
189 self.assertTrue(SimpleMatch("boo/*").match("boo/bar"))
190 self.assertTrue(SimpleMatch("boo/bar.*").match("boo/bar.foo"))
191 self.assertFalse(SimpleMatch("boo/bar.*").match("boo/barfoo"))
192
193-class MimeTypeSetTest(unittest.TestCase):
194+class MimeTypeSetTest(BaseTestCase):
195
196 def testinit(self):
197 self.assertEquals(repr(MimeTypeSet("boo", "bar", "foo")), "MimeTypeSet('bar', 'boo', 'foo')")
198
199=== modified file 'test/remote-test.py'
200--- test/remote-test.py 2010-09-15 14:20:21 +0000
201+++ test/remote-test.py 2010-09-23 11:07:07 +0000
202@@ -6,6 +6,8 @@
203 import logging
204 import signal
205 import time
206+import tempfile
207+import shutil
208 from subprocess import Popen, PIPE
209
210 # DBus setup
211@@ -18,7 +20,6 @@
212 from zeitgeist.client import ZeitgeistDBusInterface, ZeitgeistClient
213 from zeitgeist.datamodel import (Event, Subject, Interpretation, Manifestation,
214 TimeRange, StorageState)
215-from _zeitgeist.engine.remote import RemoteInterface
216
217 import testutils
218 from testutils import parse_events
219@@ -322,9 +323,31 @@
220
221 class ZeitgeistRemoteInterfaceTest(unittest.TestCase):
222
223+ def setUp(self):
224+ from _zeitgeist import engine
225+ from _zeitgeist.engine import sql, constants
226+ engine._engine = None
227+ sql.unset_cursor()
228+ self.saved_data = {
229+ "datapath": constants.DATA_PATH,
230+ "database": constants.DATABASE_FILE,
231+ "extensions": constants.USER_EXTENSION_PATH,
232+ }
233+ constants.DATA_PATH = tempfile.mkdtemp(prefix="zeitgeist.datapath.")
234+ constants.DATABASE_FILE = ":memory:"
235+ constants.USER_EXTENSION_PATH = os.path.join(constants.DATA_PATH, "extensions")
236+
237+ def tearDown(self):
238+ from _zeitgeist.engine import constants
239+ shutil.rmtree(constants.DATA_PATH)
240+ constants.DATA_PATH = self.saved_data["datapath"]
241+ constants.DATABASE_FILE = self.saved_data["database"]
242+ constants.USER_EXTENSION_PATH = self.saved_data["extensions"]
243+
244 def testQuit(self):
245 """calling Quit() on the remote interface should shutdown the
246 engine in a clean way"""
247+ from _zeitgeist.engine.remote import RemoteInterface
248 interface = RemoteInterface()
249 self.assertEquals(interface._engine.is_closed(), False)
250 interface.Quit()
251@@ -332,17 +355,21 @@
252
253 class ZeitgeistDaemonTest(unittest.TestCase):
254
255+ def setUp(self):
256+ self.env = os.environ.copy()
257+ self.datapath = tempfile.mkdtemp(prefix="zeitgeist.datapath.")
258+ self.env.update({
259+ "ZEITGEIST_DATABASE_PATH": ":memory:",
260+ "ZEITGEIST_DATA_PATH": self.datapath,
261+ })
262+
263+ def tearDown(self):
264+ shutil.rmtree(self.datapath)
265+
266 def testSIGHUP(self):
267 """sending a SIGHUP signal to a running deamon instance results
268 in a clean shutdown"""
269- daemon = Popen(
270- ["./zeitgeist-daemon.py", "--no-datahub"], stderr=PIPE, stdout=PIPE
271- )
272- # give the daemon some time to wake up
273- time.sleep(3)
274- err = daemon.poll()
275- if err:
276- raise RuntimeError("Could not start daemon, got err=%i" % err)
277+ daemon = testutils.RemoteTestCase._safe_start_daemon(env=self.env)
278 os.kill(daemon.pid, signal.SIGHUP)
279 err = daemon.wait()
280 self.assertEqual(err, 0)
281
282=== modified file 'test/run-all-tests.py'
283--- test/run-all-tests.py 2010-09-02 14:33:04 +0000
284+++ test/run-all-tests.py 2010-09-23 11:07:07 +0000
285@@ -6,36 +6,107 @@
286 import doctest
287 import logging
288 import sys
289+import tempfile
290+import shutil
291
292 from optparse import OptionParser
293-parser = OptionParser()
294-parser.add_option("-v", action="count", dest="verbosity")
295-(options, args) = parser.parse_args()
296-
297-if options.verbosity:
298- # do more fine grained stuff later
299- # redirect all debugging output to stderr
300- logging.basicConfig(stream=sys.stderr)
301-else:
302- logging.basicConfig(filename="/dev/null")
303
304 sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
305
306 # Find the test/ directory
307-testdir = os.path.dirname(os.path.abspath(__file__))
308-doctests = glob.glob(os.path.join(testdir, "*.rst"))
309-
310-# Create a test suite to run all tests
311-# first, add all doctests
312-arguments = {"module_relative": False, "globs": {"sys": sys}}
313-suite = doctest.DocFileSuite(*doctests, **arguments)
314-
315-# Add all of the tests from each file that ends with "-test.py"
316-for fname in os.listdir(testdir):
317- if fname.endswith("-test.py"):
318- fname = os.path.basename(fname)[:-3] # Get the filename and chop off ".py"
319- module = __import__(fname)
320- suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(module))
321-
322-# Run all of the tests
323-unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite)
324+TESTDIR = os.path.dirname(os.path.abspath(__file__))
325+DOCTESTS = glob.glob(os.path.join(TESTDIR, "*.rst"))
326+
327+def doctest_setup(test):
328+ test._datapath = tempfile.mkdtemp(prefix="zeitgeist.datapath.")
329+ test._env = os.environ.copy()
330+ os.environ.update({
331+ "ZEITGEIST_DATABASE_PATH": ":memory:",
332+ "ZEITGEIST_DATA_PATH": test._datapath
333+ })
334+
335+def doctest_teardown(test):
336+ shutil.rmtree(test._datapath)
337+ os.environ = test._env
338+
339+def iter_tests(suite):
340+ for test in suite:
341+ if isinstance(test, unittest.TestSuite):
342+ for t in iter_tests(test):
343+ yield t
344+ else:
345+ yield test
346+
347+def get_test_name(test):
348+ return ".".join((test.__class__.__module__, test.__class__.__name__, test._testMethodName))
349+
350+def load_tests(module, pattern):
351+ suite = unittest.defaultTestLoader.loadTestsFromModule(module)
352+ for test in iter_tests(suite):
353+ name = get_test_name(test)
354+ if pattern is not None:
355+ for p in pattern:
356+ if name.startswith(p):
357+ yield test
358+ break
359+ else:
360+ yield test
361+
362+def check_name(filename, pattern):
363+ if pattern is None:
364+ return True
365+ for p in pattern:
366+ if os.path.basename(filename).startswith(p):
367+ return True
368+ return False
369+
370+def compile_suite(pattern=None):
371+ # Create a test suite to run all tests
372+
373+ # first, add all doctests
374+ arguments = {
375+ "module_relative": False,
376+ "globs": {"sys": sys},
377+ "setUp": doctest_setup,
378+ "tearDown": doctest_teardown,
379+ }
380+ doctests = filter(lambda x: check_name(str(x), pattern), DOCTESTS)
381+ suite = doctest.DocFileSuite(*doctests, **arguments)
382+
383+ # Add all of the tests from each file that ends with "-test.py"
384+ for fname in os.listdir(TESTDIR):
385+ if fname.endswith("-test.py"):
386+ fname = os.path.basename(fname)[:-3] # Get the filename and chop off ".py"
387+ module = __import__(fname)
388+ tests = list(load_tests(module, pattern))
389+ suite.addTests(tests)
390+ return suite
391+
392+if __name__ == "__main__":
393+ parser = OptionParser()
394+ parser.add_option("-v", action="count", dest="verbosity")
395+ (options, args) = parser.parse_args()
396+
397+ if options.verbosity:
398+ # do more fine grained stuff later
399+ # redirect all debugging output to stderr
400+ logging.basicConfig(stream=sys.stderr)
401+ else:
402+ logging.basicConfig(filename="/dev/null")
403+
404+ from testutils import DBusPrivateMessageBus
405+ bus = DBusPrivateMessageBus()
406+ err = bus.run(ignore_errors=True)
407+ if err:
408+ print >> sys.stderr, "*** Failed to setup private bus, error was: %s" %err
409+ else:
410+ print >> sys.stderr, "*** Testsuite is running using a private dbus bus"
411+ config = bus.dbus_config.copy()
412+ config.update({"DISPLAY": bus.DISPLAY, "pid.Xvfb": bus.display.pid})
413+ print >> sys.stderr, "*** Configuration: %s" %config
414+ try:
415+ suite = compile_suite(args or None)
416+ # Run all of the tests
417+ unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite)
418+ finally:
419+ bus.quit(ignore_errors=True)
420
421=== modified file 'test/sql-test.py'
422--- test/sql-test.py 2010-09-19 11:49:41 +0000
423+++ test/sql-test.py 2010-09-23 11:07:07 +0000
424@@ -23,10 +23,16 @@
425 sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
426
427 import unittest
428-from _zeitgeist.engine.sql import *
429+WhereClause = None
430
431 class SQLTest (unittest.TestCase):
432
433+ def setUp(self):
434+ global WhereClause
435+ if WhereClause is None:
436+ from _zeitgeist.engine.sql import WhereClause as _WhereClause
437+ WhereClause = _WhereClause
438+
439 def testFlat (self):
440 where = WhereClause(WhereClause.AND)
441 where.add ("foo = %s", 10)
442
443=== modified file 'test/testutils.py'
444--- test/testutils.py 2010-07-22 09:52:53 +0000
445+++ test/testutils.py 2010-09-23 11:07:07 +0000
446@@ -3,6 +3,7 @@
447 # Zeitgeist
448 #
449 # Copyright © 2009 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@gmail.com>
450+# Copyright © 2009-2010 Markus Korn <thekorn@gmx.de>
451 #
452 # This program is free software: you can redistribute it and/or modify
453 # it under the terms of the GNU Lesser General Public License as published by
454@@ -22,6 +23,8 @@
455 import time
456 import sys
457 import signal
458+import tempfile
459+import shutil
460 from subprocess import Popen, PIPE
461
462 # DBus setup
463@@ -41,8 +44,6 @@
464 # maybe the user is using python < 2.6
465 import simplejson as json
466
467-from zeitgeist.datamodel import Event, Subject
468-
469 def dict2event(d):
470 ev = Event()
471 ev[0][Event.Id] = d.get("id", "").encode("UTF-8")
472@@ -74,7 +75,7 @@
473 """
474 Load a collection of JSON event definitions into 'engine'. Fx:
475
476- import_events("test/data/single_event.js", self.engine)
477+ import_events("test/data/single_event.js", self.engine)
478 """
479 events = parse_events(path)
480
481@@ -86,21 +87,50 @@
482 remote Zeitgeist process
483 """
484
485+ @staticmethod
486+ def _get_pid(matching_string):
487+ p1 = Popen(["ps", "aux"], stdout=PIPE, stderr=PIPE)
488+ p2 = Popen(["grep", matching_string], stdin=p1.stdout, stderr=PIPE, stdout=PIPE)
489+ return p2.communicate()[0]
490+
491+ @staticmethod
492+ def _safe_start_subprocess(cmd, env, timeout=1, error_callback=None):
493+ """ starts `cmd` in a subprocess and check after `timeout`
494+ if everything goes well"""
495+ process = Popen(cmd, stderr=PIPE, stdout=PIPE, env=env)
496+ # give the process some time to wake up
497+ time.sleep(timeout)
498+ error = process.poll()
499+ if error:
500+ cmd = " ".join(cmd)
501+ error = "'%s' exits with error %i." %(cmd, error)
502+ if error_callback:
503+ error += " *** %s" %error_callback(*process.communicate())
504+ raise RuntimeError(error)
505+ return process
506+
507+ @staticmethod
508+ def _safe_start_daemon(env=None, timeout=1):
509+ if env is None:
510+ env = os.environ.copy()
511+
512+ def error_callback(stdout, stderr):
513+ if "--replace" in stderr:
514+ return "%r | %s" %(stderr, RemoteTestCase._get_pid("zeitgeist-daemon").replace("\n", "|"))
515+ else:
516+ return stderr
517+
518+ return RemoteTestCase._safe_start_subprocess(
519+ ("./zeitgeist-daemon.py", "--no-datahub"), env, timeout, error_callback
520+ )
521+
522 def __init__(self, methodName):
523 super(RemoteTestCase, self).__init__(methodName)
524 self.daemon = None
525 self.client = None
526
527 def spawn_daemon(self):
528- os.environ.update({"ZEITGEIST_DATABASE_PATH": ":memory:"})
529- self.daemon = Popen(
530- ["./zeitgeist-daemon.py", "--no-datahub"], stderr=sys.stderr, stdout=sys.stderr
531- )
532- # give the daemon some time to wake up
533- time.sleep(3)
534- err = self.daemon.poll()
535- if err:
536- raise RuntimeError("Could not start daemon, got err=%i" % err)
537+ self.daemon = self._safe_start_daemon(env=self.env)
538
539 def kill_daemon(self):
540 os.kill(self.daemon.pid, signal.SIGKILL)
541@@ -109,6 +139,12 @@
542 def setUp(self):
543 assert self.daemon is None
544 assert self.client is None
545+ self.env = os.environ.copy()
546+ self.datapath = tempfile.mkdtemp(prefix="zeitgeist.datapath.")
547+ self.env.update({
548+ "ZEITGEIST_DATABASE_PATH": ":memory:",
549+ "ZEITGEIST_DATA_PATH": self.datapath,
550+ })
551 self.spawn_daemon()
552
553 # hack to clear the state of the interface
554@@ -119,6 +155,7 @@
555 assert self.daemon is not None
556 assert self.client is not None
557 self.kill_daemon()
558+ shutil.rmtree(self.datapath)
559
560 def insertEventsAndWait(self, events):
561 """
562@@ -220,3 +257,45 @@
563 num_events=num_events, result_type=result_type)
564 mainloop.run()
565 return result
566+
567+class DBusPrivateMessageBus(object):
568+ DISPLAY = ":27"
569+
570+ def _run(self):
571+ os.environ.update({"DISPLAY": self.DISPLAY})
572+ self.display = Popen(["Xvfb", self.DISPLAY, "-screen", "0", "1024x768x8"])
573+ # give the display some time to wake up
574+ time.sleep(1)
575+ err = self.display.poll()
576+ if err:
577+ raise RuntimeError("Could not start Xvfb on display %s, got err=%i" %(self.DISPLAY, err))
578+ dbus = Popen(["dbus-launch"], stdout=PIPE)
579+ time.sleep(1)
580+ self.dbus_config = dict(l.split("=", 1) for l in dbus.communicate()[0].split("\n") if l)
581+ os.environ.update(self.dbus_config)
582+
583+ def run(self, ignore_errors=False):
584+ try:
585+ return self._run()
586+ except Exception, e:
587+ if ignore_errors:
588+ return e
589+ raise
590+
591+ def _quit(self):
592+ os.kill(self.display.pid, signal.SIGKILL)
593+ self.display.wait()
594+ pid = int(self.dbus_config["DBUS_SESSION_BUS_PID"])
595+ os.kill(pid, signal.SIGKILL)
596+ try:
597+ os.waitpid(pid, 0)
598+ except OSError:
599+ pass
600+
601+ def quit(self, ignore_errors=False):
602+ try:
603+ return self._quit()
604+ except Exception, e:
605+ if ignore_errors:
606+ return e
607+ raise

Subscribers

People subscribed via source and target branches