On 8/25/07, Gregory P. Smith <[EMAIL PROTECTED]> wrote:
> I like this idea.
Yay! Now, I ain't the only one. ;)
> Be sure to have an option to ignore dependancies and run all tests.
Yes, I planned to add a such option.
> Also when skipping tests because a depedancy failed have unittest
> print out an indication that a test was skipped due to a dependancy
> rather than silently running fewer tests. Otherwise it could be
> deceptive and appear that only one test was affected.
However, that was never planned.
I added the ignore_dependencies option. Also, I fixed the sub-optimal
dependency resolution algorithm that was in my original example
implementation.
-- Alexandre
--- dep.py.old 2007-08-25 19:54:27.000000000 -0400
+++ dep.py 2007-08-25 20:02:55.000000000 -0400
@@ -2,8 +2,9 @@
class CycleError(Exception):
pass
+class TestGraph:
-class TestCase:
+ ignore_dependencies = False
def __init__(self):
self.graph = {}
@@ -19,16 +20,16 @@
graph = self.graph
toskip = set()
msgs = []
- while graph:
+ if self.ignore_dependencies:
+ for test in graph:
+ graph[test].clear()
# find tests without any pending dependencies
- source = [test for test, deps in graph.items() if not deps]
- if not source:
- raise CycleError
- for testname in source:
+ queue = [test for test, deps in graph.items() if not deps]
+ while queue:
+ testname = queue.pop()
if testname in toskip:
msgs.append("%s... skipped" % testname)
- resolvedeps(graph, testname)
- del graph[testname]
+ queue.extend(resolve(graph, testname))
continue
test = getattr(self, testname)
try:
@@ -42,8 +43,9 @@
else:
msgs.append("%s... ok" % testname)
finally:
- resolvedeps(graph, testname)
- del graph[testname]
+ queue.extend(resolve(graph, testname))
+ if graph:
+ raise CycleError
for msg in sorted(msgs):
print(msg)
@@ -60,10 +62,15 @@
rdeps.update(getrevdeps(graph, x))
return rdeps
- def resolvedeps(graph, testname):
+def resolve(graph, testname):
+ toqueue = []
for test in graph:
if testname in graph[test]:
graph[test].remove(testname)
+ if not graph[test]:
+ toqueue.append(test)
+ del graph[testname]
+ return toqueue
def depends(*args):
def decorator(test):
@@ -75,7 +82,9 @@
return decorator
-class MyTest(TestCase):
+class MyTest(TestGraph):
+
+ ignore_dependencies = True
@depends('test_foo')
def test_nah(self):
class CycleError(Exception):
pass
class TestGraph:
ignore_dependencies = False
def __init__(self):
self.graph = {}
tests = [x for x in dir(self) if x.startswith('test')]
for testname in tests:
test = getattr(self, testname)
if hasattr(test, 'deps'):
self.graph[testname] = test.deps
else:
self.graph[testname] = set()
def run(self):
graph = self.graph
toskip = set()
msgs = []
if self.ignore_dependencies:
for test in graph:
graph[test].clear()
# find tests without any pending dependencies
queue = [test for test, deps in graph.items() if not deps]
while queue:
testname = queue.pop()
if testname in toskip:
msgs.append("%s... skipped" % testname)
queue.extend(resolve(graph, testname))
continue
test = getattr(self, testname)
try:
test()
except AssertionError:
toskip.update(getrevdeps(graph, testname))
msgs.append("%s... failed" % testname)
except:
toskip.update(getrevdeps(graph, testname))
msgs.append("%s... error" % testname)
else:
msgs.append("%s... ok" % testname)
finally:
queue.extend(resolve(graph, testname))
if graph:
raise CycleError
for msg in sorted(msgs):
print(msg)
def getrevdeps(graph, testname):
"""Return the reverse depencencies of a test"""
rdeps = set()
for x in graph:
if testname in graph[x]:
rdeps.add(x)
if rdeps:
# propagate depencencies recursively
for x in rdeps.copy():
rdeps.update(getrevdeps(graph, x))
return rdeps
def resolve(graph, testname):
toqueue = []
for test in graph:
if testname in graph[test]:
graph[test].remove(testname)
if not graph[test]:
toqueue.append(test)
del graph[testname]
return toqueue
def depends(*args):
def decorator(test):
if hasattr(test, 'deps'):
test.deps.update(args)
else:
test.deps = set(args)
return test
return decorator
class MyTest(TestGraph):
ignore_dependencies = True
@depends('test_foo')
def test_nah(self):
pass
@depends('test_bar', 'test_baz')
def test_foo(self):
pass
@depends('test_tin')
def test_bar(self):
self.fail()
def test_baz(self):
self.error()
def test_tin(self):
pass
def error(self):
raise ValueError
def fail(self):
raise AssertionError
if __name__ == '__main__':
t = MyTest()
t.run()
_______________________________________________
Python-Dev mailing list
[email protected]
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe:
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com