summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Kirtland <jek@discorporate.us>2008-01-05 21:56:22 +0000
committerJason Kirtland <jek@discorporate.us>2008-01-05 21:56:22 +0000
commit68a9e6cb1fc53d6a989fa3ef6febcbe7ee304ebd (patch)
tree4a875c99e582f8fae423e0efe67463049040decc
parent40efd3a9c140ee199686becc08bfd7d7bb021ade (diff)
downloadsqlalchemy-68a9e6cb1fc53d6a989fa3ef6febcbe7ee304ebd.tar.gz
Added 'function_call_count' assertion decorator. The full-suite vs. isolated run call count discrepancy needs to be ironed out before this can be applied to zoomark.
-rw-r--r--test/testlib/profiling.py79
1 files changed, 74 insertions, 5 deletions
diff --git a/test/testlib/profiling.py b/test/testlib/profiling.py
index 947bf962e..61f6bb8f2 100644
--- a/test/testlib/profiling.py
+++ b/test/testlib/profiling.py
@@ -1,11 +1,11 @@
"""Profiling support for unit and performance tests."""
import testbase
+import os, sys
from testlib.config import parser, post_configure
import testlib.config
-import os
-__all__ = 'profiled',
+__all__ = 'profiled', 'function_call_count'
all_targets = set()
profile_config = { 'targets': set(),
@@ -19,7 +19,7 @@ def profiled(target=None, **target_opts):
@profiled('label')
or
@profiled('label', report=True, sort=('calls',), limit=20)
-
+
Enables profiling for a function when 'label' is targetted for
profiling. Report options can be supplied, and override the global
configuration and command-line options.
@@ -55,7 +55,7 @@ def profiled(target=None, **target_opts):
if not testlib.config.options.quiet:
print "Profiled target '%s', wall time: %.2f seconds" % (
target, ended - began)
-
+
report = target_opts.get('report', profile_config['report'])
if report and testlib.config.options.verbose:
sort_ = target_opts.get('sort', profile_config['sort'])
@@ -76,7 +76,7 @@ def profiled(target=None, **target_opts):
assert_range = assert_range.get(testlib.config.db, 'default')
stats = hotshot.stats.load(filename)
assert stats.total_calls >= assert_range[0] and stats.total_calls <= assert_range[1], stats.total_calls
-
+
os.unlink(filename)
return result
try:
@@ -85,3 +85,72 @@ def profiled(target=None, **target_opts):
pass
return profiled
return decorator
+
+def function_call_count(count=None, versions={}, variance=0.05):
+ """Assert a target for a test case's function call count.
+
+ count
+ Optional, general target function call count.
+
+ versions
+ Optional, a dictionary of Python version strings to counts,
+ for example::
+
+ { '2.5.1': 110,
+ '2.5': 100,
+ '2.4': 150 }
+
+ The best match for the current running python will be used.
+ If none match, 'count' will be used as the fallback.
+
+ variance
+ An +/- deviation percentage, defaults to 5%.
+ """
+
+ version_info = list(sys.version_info)
+ py_version = '.'.join([str(v) for v in sys.version_info])
+
+ while version_info:
+ version = '.'.join([str(v) for v in version_info])
+ if version in versions:
+ count = versions[version]
+ break
+ version_info.pop()
+
+ if count is None:
+ return lambda fn: fn
+
+ import hotshot, hotshot.stats
+
+ def decorator(fn):
+ def counted(*args, **kw):
+ try:
+ filename = "%s.prof" % fn.__name__
+
+ prof = hotshot.Profile(filename)
+ prof.start()
+ try:
+ result = fn(*args, **kw)
+ finally:
+ prof.stop()
+ prof.close()
+
+ stats = hotshot.stats.load(filename)
+ calls = stats.total_calls
+ deviance = int(count * variance)
+ if (calls < (count - deviance) or
+ calls > (count + deviance)):
+ raise AssertionError(
+ "Function call count %s not within %s%% "
+ "of expected %s. (Python version %s)" % (
+ calls, variance, count, py_version))
+ return result
+ finally:
+ if os.path.exists(filename):
+ os.unlink(filename)
+ try:
+ counted.__name__ = fn.__name__
+ except:
+ pass
+ return counted
+ return decorator