Skip to content

Commit 690e604

Browse files
committed
Switch testcases to werkzeug format.
1 parent 2961ee8 commit 690e604

14 files changed

+329
-104
lines changed

run-tests.py

Lines changed: 2 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,5 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
3-
"""Test runner for tmuxp project. ``$ ./run-tests.py --help`` for more."""
43

5-
from __future__ import absolute_import, division, print_function, with_statement
6-
7-
try:
8-
import unittest2 as unittest
9-
except ImportError: # Python 2.7
10-
import unittest
11-
import sys
12-
import os
13-
import argparse
14-
15-
tmux_path = sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
16-
if tmux_path not in sys.path:
17-
sys.path.insert(0, tmux_path)
18-
19-
20-
def main(verbosity=2, failfast=False):
21-
"""Run TestSuite in new tmux session. Exit with code 0 if success."""
22-
23-
suites = unittest.TestLoader().discover('tmuxp.testsuite', pattern="test_*.py")
24-
result = unittest.TextTestRunner(
25-
verbosity=verbosity, failfast=failfast).run(suites)
26-
if result.wasSuccessful():
27-
sys.exit(0)
28-
else:
29-
sys.exit(1)
30-
31-
if __name__ == '__main__':
32-
parser = argparse.ArgumentParser(
33-
description='''\
34-
Run tests suite for tmuxp. With no arguments, runs all test suites in tmuxp.testsuite.
35-
36-
Default usage:
37-
$ ./run-tests.py
38-
''',
39-
formatter_class=argparse.RawTextHelpFormatter
40-
)
41-
parser.add_argument(
42-
'--tests',
43-
nargs='*',
44-
default=None,
45-
help='''\
46-
Test individual, TestCase or TestSuites, or multiple. Example for test_config TestSuite:
47-
48-
by TestSuite (module):
49-
$ ./run-tests.py test_config
50-
51-
by TestCase:
52-
$ ./run-tests.py test_config.ImportExportTest
53-
individual tests:
54-
$ ./run-tests.py test_config.ImportExportTest.test_export_json
55-
56-
Multiple can be separated by spaces:
57-
$ ./run-tests.py test_config.ImportExportTest.test_export_json \\
58-
test_config.ImportExportTest.test_window
59-
60-
./run-tests will automatically assume the package namespace ``tmuxp.testsuite``.
61-
62-
$ ./run-tests.py test_config.ImportExportTest
63-
64-
is the same as:
65-
66-
$ ./run-tests.py tmuxp.testsuite.test_config.ImportExportTest
67-
'''
68-
)
69-
parser.add_argument('-l', '--log-level', dest='log_level', default='INFO',
70-
help='Log level')
71-
parser.add_argument(
72-
'-v', '--verbosity', dest='verbosity', type=int, default=2,
73-
help='unittest verbosity level')
74-
parser.add_argument(
75-
'-F', '--failfast', dest='failfast', action='store_true',
76-
77-
help='Stop on first test failure. failfast=True')
78-
args = parser.parse_args()
79-
80-
verbosity = args.verbosity
81-
82-
import logging
83-
logging.getLogger('tmuxp.testsuite').setLevel(args.log_level.upper())
84-
85-
if 'help' in args:
86-
parser.print_help()
87-
if args.tests and len(args.tests) > int(0):
88-
for arg in args.tests:
89-
if not arg.startswith('tmuxp.testsuite'):
90-
loc = args.tests.index(arg)
91-
args.tests[loc] = 'tmuxp.testsuite.%s' % arg
92-
suites = unittest.TestLoader().loadTestsFromNames(args.tests)
93-
result = unittest.TextTestRunner(
94-
verbosity=verbosity, failfast=args.failfast).run(suites)
95-
else:
96-
main(verbosity=verbosity, failfast=args.failfast)
4+
from tmuxp.testsuite import main
5+
main()

tmuxp/_compat.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from io import StringIO, BytesIO
2121
import pickle
22+
import configparser
2223

2324
izip = zip
2425
imap = map
@@ -31,6 +32,8 @@
3132
import urllib.parse as urllib
3233
import urllib.parse as urlparse
3334

35+
exec('def reraise(tp, value, tb=None):\n raise(tp, value, tb)')
36+
3437
console_encoding = sys.__stdout__.encoding
3538

3639
def console_to_str(s):
@@ -54,6 +57,7 @@ def console_to_str(s):
5457
from cStringIO import StringIO as BytesIO
5558
from StringIO import StringIO
5659
import cPickle as pickle
60+
import ConfigParser as configparser
5761

5862
from itertools import izip, imap
5963
range_type = xrange
@@ -67,5 +71,10 @@ def console_to_str(s):
6771
def console_to_str(s):
6872
return s.decode('utf_8')
6973

74+
def reraise(tp, value, tb=None):
75+
if value.__traceback__ is not tb:
76+
raise(value.with_traceback(tb))
77+
raise value
78+
7079

7180
number_types = integer_types + (float,)

tmuxp/testsuite/__init__.py

Lines changed: 207 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,23 @@
55
~~~~~~~~~~~
66
77
"""
8-
98
from __future__ import absolute_import, division, print_function, \
109
with_statement, unicode_literals
1110

12-
from ..server import Server
13-
t = Server()
14-
t.socket_name = 'tmuxp_test'
11+
import logging
12+
import unittest
13+
import sys
1514

1615
from .. import log
17-
import logging
16+
from .._compat import string_types, PY2, reraise
17+
from ..server import Server
18+
19+
import pkgutil
20+
1821
logger = logging.getLogger()
1922

23+
t = Server()
24+
t.socket_name = 'tmuxp_test'
2025

2126
if not logger.handlers:
2227
channel = logging.StreamHandler()
@@ -30,11 +35,202 @@
3035
testsuite_logger.setLevel('INFO')
3136

3237

33-
def suite():
34-
"""Return TestSuite."""
38+
class ImportStringError(ImportError):
39+
"""Provides information about a failed :func:`import_string` attempt."""
40+
41+
#: String in dotted notation that failed to be imported.
42+
import_name = None
43+
#: Wrapped exception.
44+
exception = None
45+
46+
def __init__(self, import_name, exception):
47+
self.import_name = import_name
48+
self.exception = exception
49+
50+
msg = (
51+
'import_string() failed for %r. Possible reasons are:\n\n'
52+
'- missing __init__.py in a package;\n'
53+
'- package or module path not included in sys.path;\n'
54+
'- duplicated package or module name taking precedence in '
55+
'sys.path;\n'
56+
'- missing module, class, function or variable;\n\n'
57+
'Debugged import:\n\n%s\n\n'
58+
'Original exception:\n\n%s: %s')
59+
60+
name = ''
61+
tracked = []
62+
for part in import_name.replace(':', '.').split('.'):
63+
name += (name and '.') + part
64+
imported = import_string(name, silent=True)
65+
if imported:
66+
tracked.append((name, getattr(imported, '__file__', None)))
67+
else:
68+
track = ['- %r found in %r.' % (n, i) for n, i in tracked]
69+
track.append('- %r not found.' % name)
70+
msg = msg % (import_name, '\n'.join(track),
71+
exception.__class__.__name__, str(exception))
72+
break
73+
74+
ImportError.__init__(self, msg)
75+
76+
def __repr__(self):
77+
return '<%s(%r, %r)>' % (self.__class__.__name__, self.import_name,
78+
self.exception)
79+
80+
81+
def import_string(import_name, silent=False):
82+
"""Imports an object based on a string. This is useful if you want to
83+
use import paths as endpoints or something similar. An import path can
84+
be specified either in dotted notation (``xml.sax.saxutils.escape``)
85+
or with a colon as object delimiter (``xml.sax.saxutils:escape``).
86+
87+
If `silent` is True the return value will be `None` if the import fails.
88+
89+
:param import_name: the dotted name for the object to import.
90+
:param silent: if set to `True` import errors are ignored and
91+
`None` is returned instead.
92+
:return: imported object
93+
"""
94+
#XXX: py3 review needed
95+
assert isinstance(import_name, string_types)
96+
# force the import name to automatically convert to strings
97+
import_name = str(import_name)
3598
try:
36-
import unittest2 as unittest
37-
except ImportError: # Python 2.7
38-
import unittest
99+
if ':' in import_name:
100+
module, obj = import_name.split(':', 1)
101+
elif '.' in import_name:
102+
module, obj = import_name.rsplit('.', 1)
103+
else:
104+
return __import__(import_name)
105+
# __import__ is not able to handle unicode strings in the fromlist
106+
# if the module is a package
107+
if PY2 and isinstance(obj, unicode):
108+
obj = obj.encode('utf-8')
109+
try:
110+
return getattr(__import__(module, None, None, [obj]), obj)
111+
except (ImportError, AttributeError):
112+
# support importing modules not yet set up by the parent module
113+
# (or package for that matter)
114+
modname = module + '.' + obj
115+
__import__(modname)
116+
return sys.modules[modname]
117+
except ImportError as e:
118+
if not silent:
119+
reraise(
120+
ImportStringError,
121+
ImportStringError(import_name, e),
122+
sys.exc_info()[2])
123+
124+
125+
def find_modules(import_path, include_packages=False, recursive=False):
126+
"""Find all the modules below a package. This can be useful to
127+
automatically import all views / controllers so that their metaclasses /
128+
function decorators have a chance to register themselves on the
129+
application.
130+
131+
Packages are not returned unless `include_packages` is `True`. This can
132+
also recursively list modules but in that case it will import all the
133+
packages to get the correct load path of that module.
134+
135+
:param import_name: the dotted name for the package to find child modules.
136+
:param include_packages: set to `True` if packages should be returned, too.
137+
:param recursive: set to `True` if recursion should happen.
138+
:return: generator
139+
"""
140+
module = import_string(import_path)
141+
path = getattr(module, '__path__', None)
142+
if path is None:
143+
raise ValueError('%r is not a package' % import_path)
144+
basename = module.__name__ + '.'
145+
for importer, modname, ispkg in pkgutil.iter_modules(path):
146+
modname = basename + modname
147+
if ispkg:
148+
if include_packages:
149+
yield modname
150+
if recursive:
151+
for item in find_modules(modname, include_packages, True):
152+
yield item
153+
else:
154+
yield modname
155+
156+
157+
def iter_suites(package):
158+
"""Yields all testsuites."""
159+
for module in find_modules(package, include_packages=True):
160+
mod = __import__(module, fromlist=['*'])
161+
if hasattr(mod, 'suite'):
162+
yield mod.suite()
163+
164+
165+
def find_all_tests(suite):
166+
"""Yields all the tests and their names from a given suite."""
167+
suites = [suite]
168+
while suites:
169+
s = suites.pop()
170+
try:
171+
suites.extend(s)
172+
except TypeError:
173+
yield s, '%s.%s.%s' % (
174+
s.__class__.__module__,
175+
s.__class__.__name__,
176+
s._testMethodName
177+
)
178+
179+
180+
class BetterLoader(unittest.TestLoader):
181+
"""A nicer loader that solves two problems. First of all we are setting
182+
up tests from different sources and we're doing this programmatically
183+
which breaks the default loading logic so this is required anyways.
184+
Secondly this loader has a nicer interpolation for test names than the
185+
default one so you can just do ``run-tests.py ViewTestCase`` and it
186+
will work.
187+
"""
39188

40-
return unittest.TestLoader().discover('.', pattern="test_*.py")
189+
def getRootSuite(self):
190+
return suite()
191+
192+
def loadTestsFromName(self, name, module=None):
193+
root = self.getRootSuite()
194+
if name == 'suite':
195+
return root
196+
197+
all_tests = []
198+
for testcase, testname in find_all_tests(root):
199+
if testname == name or \
200+
testname.endswith('.' + name) or \
201+
('.' + name + '.') in testname or \
202+
testname.startswith(name + '.'):
203+
all_tests.append(testcase)
204+
205+
if not all_tests:
206+
raise LookupError('could not find test case for "%s"' % name)
207+
208+
if len(all_tests) == 1:
209+
return all_tests[0]
210+
rv = unittest.TestSuite()
211+
for test in all_tests:
212+
rv.addTest(test)
213+
return rv
214+
215+
216+
def suite():
217+
"""A testsuite that has all the Flask tests. You can use this
218+
function to integrate the Flask tests into your own testsuite
219+
in case you want to test that monkeypatches to Flask do not
220+
break it.
221+
"""
222+
suite = unittest.TestSuite()
223+
for other_suite in iter_suites(__name__):
224+
suite.addTest(other_suite)
225+
return suite
226+
227+
228+
def main():
229+
"""Runs the testsuite as command line application."""
230+
try:
231+
unittest.main(testLoader=BetterLoader(), defaultTest='suite')
232+
except Exception:
233+
import sys
234+
import traceback
235+
traceback.print_exc()
236+
sys.exit(1)

tmuxp/testsuite/test_cli.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import shutil
1414
import tempfile
1515
import logging
16+
import unittest
1617

1718
import kaptan
1819

@@ -179,3 +180,10 @@ def tearDownClass(cls):
179180
}
180181
]
181182
}
183+
184+
185+
def suite():
186+
suite = unittest.TestSuite()
187+
suite.addTest(unittest.makeSuite(FindConfigsTest))
188+
suite.addTest(unittest.makeSuite(StartupTest))
189+
return suite

0 commit comments

Comments
 (0)