1 # package is named tests, not test, so it won't be confused with test in stdlib
2 from __future__ import print_function
18 from eventlet import tpool
21 # convenience for importers
26 """ Decorator that marks a function as skipped. Uses nose's SkipTest exception
27 if installed. Without nose, this will count skipped tests as passing tests."""
29 from nose.plugins.skip import SkipTest
33 skipme.__name__ = func.__name__
36 # no nose, we'll just skip the test ourselves
38 print(("Skipping {0}".format(func.__name__)))
39 skipme.__name__ = func.__name__
43 def skip_if(condition):
44 """ Decorator that skips a test if the *condition* evaluates True.
45 *condition* can be a boolean or a callable that accepts one argument.
46 The callable will be called with the function to be decorated, and
47 should return True to skip the test.
49 def skipped_wrapper(func):
50 def wrapped(*a, **kw):
51 if isinstance(condition, bool):
54 result = condition(func)
56 return skipped(func)(*a, **kw)
59 wrapped.__name__ = func.__name__
61 return skipped_wrapper
64 def skip_unless(condition):
65 """ Decorator that skips a test if the *condition* does not return True.
66 *condition* can be a boolean or a callable that accepts one argument.
67 The callable will be called with the function to be decorated, and
68 should return True if the condition is satisfied.
70 def skipped_wrapper(func):
71 def wrapped(*a, **kw):
72 if isinstance(condition, bool):
75 result = condition(func)
77 return skipped(func)(*a, **kw)
80 wrapped.__name__ = func.__name__
82 return skipped_wrapper
85 def requires_twisted(func):
86 """ Decorator that skips a test if Twisted is not present."""
88 from eventlet.hubs import get_hub
90 return 'Twisted' in type(get_hub()).__name__
93 return skip_unless(requirement)(func)
96 def using_pyevent(_f):
97 from eventlet.hubs import get_hub
98 return 'pyevent' in type(get_hub()).__module__
101 def skip_with_pyevent(func):
102 """ Decorator that skips a test if we're using the pyevent hub."""
103 return skip_if(using_pyevent)(func)
106 def skip_on_windows(func):
107 """ Decorator that skips a test on Windows."""
108 return skip_if(sys.platform.startswith('win'))(func)
111 def skip_if_no_itimer(func):
112 """ Decorator that skips a test if the `itimer` module isn't found """
119 return skip_unless(has_itimer)(func)
122 def skip_if_no_ssl(func):
123 """ Decorator that skips a test if SSL is not available."""
125 import eventlet.green.ssl
129 import eventlet.green.OpenSSL
135 class TestIsTakingTooLong(Exception):
136 """ Custom exception class to be raised when a test's runtime exceeds a limit. """
140 class LimitedTestCase(unittest.TestCase):
141 """ Unittest subclass that adds a timeout to all tests. Subclasses must
142 be sure to call the LimitedTestCase setUp and tearDown methods. The default
143 timeout is 1 second, change it by setting TEST_TIMEOUT to the desired
149 self.previous_alarm = None
150 self.timer = eventlet.Timeout(self.TEST_TIMEOUT,
151 TestIsTakingTooLong(self.TEST_TIMEOUT))
153 def reset_timeout(self, new_timeout):
154 """Changes the timeout duration; only has effect during one test.
155 `new_timeout` can be int or float.
158 self.timer = eventlet.Timeout(new_timeout,
159 TestIsTakingTooLong(new_timeout))
161 def set_alarm(self, new_timeout):
162 """Call this in the beginning of your test if you expect busy loops.
163 Only has effect during one test.
164 `new_timeout` must be int.
166 def sig_alarm_handler(sig, frame):
167 # Could arm previous alarm but test is failed anyway
168 # seems to be no point in restoring previous state.
169 raise TestIsTakingTooLong(new_timeout)
171 self.previous_alarm = (
172 signal.signal(signal.SIGALRM, sig_alarm_handler),
173 signal.alarm(new_timeout),
178 if self.previous_alarm:
179 signal.signal(signal.SIGALRM, self.previous_alarm[0])
180 signal.alarm(self.previous_alarm[1])
187 def assert_less_than(self, a, b, msg=None):
188 msg = msg or "%s not less than %s" % (a, b)
191 assertLessThan = assert_less_than
193 def assert_less_than_equal(self, a, b, msg=None):
194 msg = msg or "%s not less than or equal to %s" % (a, b)
197 assertLessThanEqual = assert_less_than_equal
200 def check_idle_cpu_usage(duration, allowed_part):
202 # TODO: use https://code.google.com/p/psutil/
203 from nose.plugins.skip import SkipTest
204 raise SkipTest('CPU usage testing not supported (`import resource` failed)')
206 r1 = resource.getrusage(resource.RUSAGE_SELF)
207 eventlet.sleep(duration)
208 r2 = resource.getrusage(resource.RUSAGE_SELF)
209 utime = r2.ru_utime - r1.ru_utime
210 stime = r2.ru_stime - r1.ru_stime
211 assert utime + stime < duration * allowed_part, \
212 "CPU usage over limit: user %.0f%% sys %.0f%% allowed %.0f%%" % (
213 utime / duration * 100, stime / duration * 100,
217 def verify_hub_empty():
218 from eventlet import hubs
220 num_readers = len(hub.get_readers())
221 num_writers = len(hub.get_writers())
222 num_timers = hub.get_timers_count()
223 assert num_readers == 0 and num_writers == 0, "Readers: %s Writers: %s" % (num_readers, num_writers)
226 def find_command(command):
227 for dir in os.getenv('PATH', '/usr/bin:/usr/sbin').split(os.pathsep):
228 p = os.path.join(dir, command)
229 if os.access(p, os.X_OK):
231 raise IOError(errno.ENOENT, 'Command not found: %r' % command)
234 def silence_warnings(func):
235 def wrapper(*args, **kw):
236 warnings.simplefilter('ignore', DeprecationWarning)
238 return func(*args, **kw)
240 warnings.simplefilter('default', DeprecationWarning)
241 wrapper.__name__ = func.__name__
245 def get_database_auth():
246 """Retrieves a dict of connection parameters for connecting to test databases.
248 Authentication parameters are highly-machine specific, so
249 get_database_auth gets its information from either environment
250 variables or a config file. The environment variable is
251 "EVENTLET_DB_TEST_AUTH" and it should contain a json object. If
252 this environment variable is present, it's used and config files
253 are ignored. If it's not present, it looks in the local directory
254 (tests) and in the user's home directory for a file named
255 ".test_dbauth", which contains a json map of parameters to the
260 'MySQLdb': {'host': 'localhost', 'user': 'root', 'passwd': ''},
261 'psycopg2': {'user': 'test'},
267 import simplejson as json
269 print("No json implementation, using baked-in db credentials.")
272 if 'EVENTLET_DB_TEST_AUTH' in os.environ:
273 return json.loads(os.environ.get('EVENTLET_DB_TEST_AUTH'))
275 files = [os.path.join(os.path.dirname(__file__), '.test_dbauth'),
276 os.path.join(os.path.expanduser('~'), '.test_dbauth')]
279 auth_utf8 = json.load(open(f))
280 # Have to convert unicode objects to str objects because
281 # mysqldb is dum. Using a doubly-nested list comprehension
282 # because we know that the structure is a two-level dict.
284 [(str(modname), dict(
285 [(str(k), str(v)) for k, v in connectargs.items()]))
286 for modname, connectargs in auth_utf8.items()])
292 def run_python(path):
293 if not path.endswith('.py'):
295 path = os.path.abspath(path)
296 dir_ = os.path.dirname(path)
297 new_env = os.environ.copy()
298 new_env['PYTHONPATH'] = os.pathsep.join(sys.path + [dir_])
299 p = subprocess.Popen(
300 [sys.executable, path],
302 stderr=subprocess.STDOUT,
303 stdin=subprocess.PIPE,
304 stdout=subprocess.PIPE,
306 output, _ = p.communicate()
310 certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt')
311 private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key')